ファイルのアップロードはC#では簡単である。 ID、パスワードでアクセス管理されているサーバーへアップロードするには、次のようにすればよい。
WebClient wc = new WebClient(); wc.Credentials = new NetworkCredential("id", "password"); wc.UploadFile(uri, localpath);
NetworkCredentialコンストラクタの引数にはアクセス権のあるIDとパスワードを文字列型で与える。
ディレクトリの作成は FTPサーバー限定である。
ディレクトリの作成の方が少し面倒である。 アップロードと同じWebClientクラスのインスタンスで実行できた方がありがたいのであるが、 マニュアルをざっとみた限りでは、該当機能が見つからなかった。
WebRequestクラスを使って、次のようにする。
ディレクトリの削除はMethodプロパティに WebRequestMethods.Ftp.RemoveDirectory
を指定する。
ファイルの削除はMethodプロパティに WebRequestMethods.Ftp.DeleteFile
を指定する。
WebRequest request = WebRequest.Create(uri); request.Method = WebRequestMethods.Ftp.MakeDirectory; request.Credentials = new NetworkCredential("id", "password"); using (var resp = (FtpWebResponse)request.GetResponse()) { Console.WriteLine(resp.StatusCode); }
FTPサイトのディレクトリやファイルの照合は行わず、 前回アップロードを実行した時刻以降に作成、更新したファイルのみアップロードしている。 新たなディレクトリがあれば、FTPサイトに作成している。
したがって、ディレクトリやファイルの削除には対応していない。 これは、削除が少ないことと、削除に対応するとプログラムの実行時間が長くなることによる。
新規ディレクトリが見つかると、ファイルのアップロードとは別に ディレクトリ毎にWebRequestインスタンスを生成して、FTPサイトにディレクトリを作成している。 多くの新規ディレクトリが存在する場合、効率が悪いことになるが、実際の運用では、 ファイルの新規作成や更新が殆どであり、ディレクトリの作成はごくまれなため、問題にならない。
// upload.cs 2023.1.19/2020.3.21/2013.3.9 // 前回アップロードした時刻以降で更新または作成された // ファイルをサーバーにアップロードする。 // csc /out:c:\_wwwsys\bin\upload.exe c:\_wwwsys\src\upload.cs using System; using System.IO; using System.Net; class Upload { const string DIR = @"c:\_www"; const string TimeStampFile = "c:/_wwwsys/timestamp.dat"; string srv, id, pw; DateTime dtLast; WebClient wc; public Upload() { Console.Write("password:"); pw = Console.ReadLine(); if (File.Exists(TimeStampFile)) { dtLast = File.GetLastWriteTime(TimeStampFile); // 前回のアップロード時間 File.SetLastWriteTime(TimeStampFile, DateTime.Now); // タイムスタンプ更新 } else { dtLast = new DateTime(1,1,1); File.WriteAllText(TimeStampFile, ""); } try { srv = "ftp://htdmnr.starfree.jp"; id = "htdmnr.starfree.jp"; wc = new WebClient(); wc.Credentials = new NetworkCredential(id, pw); scan(DIR); wc.Dispose(); } catch (Exception e) { Console.WriteLine(e); } } void scan(string dir) { foreach (string path in Directory.GetFiles(dir)) { string uri = srv + path.Substring(DIR.Length).Replace(@"\", "/"); DateTime dtCR = Directory.GetCreationTime(path); DateTime dtLW = Directory.GetLastWriteTime(path); if (dtCR > dtLast || dtLW > dtLast) { // 更新または新規ファイル try { byte[] response = wc.UploadFile(uri, path); Console.WriteLine("upload: " + path + " " + System.Text.Encoding.ASCII.GetString(response)); } catch (Exception e) { System.Windows.Forms.MessageBox.Show(e.ToString()); } } } foreach (string path in Directory.GetDirectories(dir)) { string uri = srv + path.Substring(DIR.Length).Replace(@"\", "/"); DateTime dt = Directory.GetCreationTime(path); if (dt > dtLast) { Console.WriteLine("新規ディレクトリ: " + uri); try { WebRequest request = WebRequest.Create(uri); request.Method = WebRequestMethods.Ftp.MakeDirectory; request.Credentials = new NetworkCredential(id, pw); using (var resp = (FtpWebResponse)request.GetResponse()) { Console.WriteLine(resp.StatusCode); } } catch (Exception ex) { Console.WriteLine(ex); } } scan(path); } } static void Main() { new Upload(); System.Threading.Thread.Sleep(1000); } }
ファイル query.txt のサイズが小さい時は 次のようにしてデータをサーバーへ送信できる。
curl --globoff -o output.xml -d @c:/osm/query.txt http://overpass-api.de/api/interpreter
curlコマンドでは、送信データを一旦メモリに読み込むようであり、Postするファイルサイズが大きい場合、 メモリ不足になる。
記事[1]を参考にして、次のプログラムを作成した。
using System; using System.IO; using System.Net; class MyWebClient : WebClient { protected override WebRequest GetWebRequest(Uri address) { var w = base.GetWebRequest(address); w.Timeout = 60 * 60 * 1000; return w; } } class Post { static void Main(string[] args) { string url = "http://overpass-api.de/api/interpreter"; string str = File.ReadAllText("c:/osm/nodes.txt", System.Text.Encoding.GetEncoding("utf-8")); MyWebClient wc = new MyWebClient(); //データを送信し、また受信する string resText = wc.UploadString(url, str); wc.Dispose(); //受信したデータを表示する Console.WriteLine(resText); } }
しかし、次のエラーとなった。
ハンドルされていない例外: System.Net.WebException: リモート サーバーがエラーを返しました: (400) 要求が不適切です 場所 System.Net.WebClient.UploadDataInternal(Uri address, String method, Byte[] data, WebRequest& request) 場所 System.Net.WebClient.UploadString(Uri address, String method, String data) 場所 Post.Main(String[] args)
curl コマンドでの --globoff は送信データに [ ] や { } があることを伝えるもの[2]であり、 WebClient では考慮が要らない。
node数が多すぎるためかも知れない。まず リソースの上限の設定方法に問題がありそうである。
Overpass XML では element-limit があるが、Overpass QL にはないようだ。maxsize はある。要素数ではなく、 メモリサイズかも知れない。
リソースの制限がない場合、data=node(1);out; で良かったが、リソースの上限を設定する場合、「data=」を 取って、 [timeout:3600][maxsize:2073741824]; node(id:123456,234567, .... , 987654);