ファイルのアップロードは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);