- 題名: FTPサーバーからのファイルのダウンロードについて
- 日時: 2009/12/20 11:54:45
- ID: 26108
- この記事の返信元:
- (なし)
- この記事への返信:
- [26109] Re[1]: FTPサーバーからのファイルのダウンロードについて2009/12/20 12:36:09
- ツリーを表示
■No26114に返信(マメオさんの記事)
FTP サーバーから返される一覧の内容は、サーバーに依存しています。
そのため、汎用的なコードを記述する事は非常に大変です。
たとえば、当日分であれば年表示を省略するサーバー、
当日分以外は、時刻部を出力しないサーバー、
当日分だと日付部を出力しないサーバー、
時刻を24時間表記で出力するサーバー、
時刻を12時間表記し、午後帯には末尾にPの文字を付けるサーバー、
10時より前の時刻の場合、時刻部を1桁で表示するサーバー、
10時より前の時刻の場合、時刻部を先頭ゼロ埋め2桁で表示するサーバー、
10時より前の時刻の場合、時刻部を先頭空白埋め2桁で表示するサーバー、
日付にタイムゾーンを用いて表示するサーバーなどなどなど。
> 出力結果です↓↓
解析対象をこのサーバーに限定して良いのであれば、
たとえばこんな感じでしょうか。
細かい表記仕様が分からないので、解析処理の部分は適宜補ってみてください。
'テストデータ。実際にはFTPサーバーから取得される文字列。
Dim ftpResult As String = <![CDATA[-rwxrwxrwx 1 0 0 36768 Sep 15 10:35 081217_***.csv
-rwxrwxrwx 1 0 0 625651 Oct 07 2008 ***.aaa.CSV
-rwxrwxrwx 1 0 0 182030 Oct 07 2008 ***.ABC.CSV
-rwxrwxrwx 1 0 0 2708 Oct 07 2008 ***.aaa.CSV
-rwxrwxrwx 1 0 0 588627 Oct 07 2008 ***.aaa.CSV
-rwxrwxrwx 1 0 0 175894 Oct 07 2008 ***.ABC.CSV
-rwxrwxrwx 1 0 0 2711 Oct 07 2008 ***.aaa.CSV]]>.Value
'サーバー側の改行コードが分からないので、一度 vbNewLine 揃えておく。
ftpResult = Replace(ftpResult, vbCrLf, vbLf).Replace(vbCr, vbLf).Replace(vbLf, vbNewLine)
'行単位に置換
Dim lines() As String = Split(ftpResult, vbNewLine)
'予期される日付形式
Dim datePattern() As String = {"MMM dd yyyy", "MMM dd hh:mm", "MMM dd h:mm", "MMM dd h:mm"}
Dim ci = System.Globalization.CultureInfo.InvariantCulture
Dim dts = System.Globalization.DateTimeStyles.None
'ファイル名と日付部だけ取りだす
Dim q = From line In Array.ConvertAll(lines, Function(s) Mid(s, 44)) _
Select line, pos = InStrRev(line, " ") _
Select Name = Mid(line, pos + 1), [Date] = _
Date.ParseExact(Left(line, pos - 1), datePattern, ci, dts)
'取りだした一覧から、最大日付のファイル名を選ぶ
Dim lastUpdatedFile As String = If(q.Count = 0, Nothing, q.OrderBy(Function(x) x.Date).First().Name)
Console.WriteLine(lastUpdatedFile)
…もしかしたら、質問の意図を読み違えていたかも。 ■No26114に返信(マメオさんの記事) > ---------------------------------------------------------------------- > -rwxrwxrwx 1 0 0 36768 Sep 15 10:35 081217_***.csv > -rwxrwxrwx 1 0 0 625651 Oct 07 2008 ***.aaa.CSV > -rwxrwxrwx 1 0 0 182030 Oct 07 2008 ***.ABC.CSV > -rwxrwxrwx 1 0 0 2708 Oct 07 2008 ***.aaa.CSV > -rwxrwxrwx 1 0 0 588627 Oct 07 2008 ***.aaa.CSV > -rwxrwxrwx 1 0 0 175894 Oct 07 2008 ***.ABC.CSV > -rwxrwxrwx 1 0 0 2711 Oct 07 2008 ***.aaa.CSV > ---------------------------------------------------------------------- > ファイル一覧のほんの一部ですが、この中から最新の「***.ABC.CSV」だけを > 抽出しダウンロードできればと考えています。 見たところ、「***.ABC.CSV」が複数ありますね。 で、『最新』を抽出したいとの事ですが、提示された結果を見る限り、 081217_***.csv のみが "????/09/15 10:35:??" となっていて、その他の ***.ABC.CSV (等)は全て"2008/10/07 ??:??:??" となっていますよね。 時刻部が不足しているこの結果から、[***.aaa.CSV] と [***.ABC.CSV] のうち、 どちらがより新しいかを見分ける事は不可能と言えるかと思います。 ただし、その一覧が "LIST" (ListDirectoryDetails) コマンドで得た物なら、 さらにそのファイル名を用いて、"MDTM" (GetDateTimestamp) コマンドを 再発行することで、個々のファイルの更新日時を得られる可能性があります。 (サーバー側の対応も必要なので保証はできかねますが) もし、MDTM でも取得できない場合には、そもそもその FTP サーバーが、 日時をより正確に取得するコマンドを実装していないかどうかを 管理者に問い合わせてみてください。 (そもそもサーバーが実装していないのであれば、今回の件は実現は不可能です) なお、日付を正しく取得するコマンドが WebRequestMethods.Ftp に定義されて いない場合には、FtpWebRequest 以外の方法に切り替える必要が生じます。
■No26219に返信(マメオさんの記事)
> 出力結果を整理してみました。↓↓
ファイル情報は、先頭が "-" で始まる行ですよね。
それ以外の行(ディレクトリ情報、空行、最終行)を除去する必要がありそうです。
そして、日付書式の見直しも必要です。
今回の場合、24時間表記なので、予期される日付形式は
"MMM dd yyyy" と "MMM dd HH:mm" だけで良いでしょう。
以上 2 点を直せば、動作すると思います。
'%WINDIR%\Microsoft.NET\Framework\v3.5\VBC.EXE /out:sample.exe sample.vb
Imports System
Imports System.Globalization
Imports Microsoft.VisualBasic
Module Test
Sub Main()
'FTPサーバーから取得される文字列
Dim ftpResult As String = <![CDATA[-rwxrwxrwx 1 0 0 36768 Sep 15 10:35 ***.aaa.CSV
-rwxrwxrwx 1 0 0 2708 Oct 07 2008 ***.aaa.CSV
-rw-r--r-- 1 502 503 51873 Sep 15 10:35 ***.aaa.CSV
-rwxrwxrwx 1 0 0 2797 Feb 06 2009 ***.aaa.CSV
-rw-r--r-- 1 502 503 471593 Feb 19 2009 ***.aaa.CSV
-rw-r--r-- 1 502 503 143957 May 18 2009 ***.ABC.CSV
-rw-r--r-- 1 502 503 49027 Sep 03 21:22 ***.aaa.CSV
-rw-r--r-- 1 502 503 183830 Sep 03 21:22 ***.ABC.CSV
-rw-r--r-- 1 505 506 70188 Dec 03 07:36 ***.aaa.CSV
-rw-r--r-- 1 505 506 207747 Dec 03 07:36 ***.ABC.CSV
-rw-r--r-- 1 505 506 3355 Jan 08 21:20 ***.aaa.CSV
-rwxrwxrwx 1 0 0 109315 Oct 22 2008 ***.aaa.CSV
-rwxrwxrwx 1 0 0 2807 Feb 01 2009 ***.aaa.CSV
-rw-r--r-- 1 502 503 53601 Feb 13 2009 ***.aaa.CSV
-rw-r--r-- 1 505 506 3351 Nov 18 17:34 ***.aaa.CSV
drwxrwxrwx 2 0 0 69632 Jan 08 21:20 edp]]>.Value
'改行コードを vbCrLf に揃える
ftpResult = Replace(ftpResult, vbCrLf, vbLf).Replace(vbCr, vbLf).Replace(vbLf, vbCrLf)
'空行を削除
ftpResult = ftpResult.Replace(vbLf & vbCr, "")
'行単位に置換
Dim lines() As String = Split(ftpResult, vbNewLine)
'予期される日付形式
Dim datePattern() As String = {"MMM dd yyyy", "MMM dd HH:mm"}
Dim ci = CultureInfo.InvariantCulture
Dim dts = DateTimeStyles.None
'ファイル名と日付部だけ取りだす
Dim q = From line In Array.ConvertAll(lines, Function(s) Mid(s, 44)) _
Select line, pos = InStrRev(line, " ") _
Select Name = Mid(line, pos + 1), [Date] = _
Date.ParseExact(line.Substring(0, pos - 1), datePattern, ci, dts)
'取りだした一覧から、最大日付のファイル名を選ぶ
Dim lastUpdatedFile As String = If(q.Count = 0, Nothing, q.OrderBy(Function(x) x.Date).First().Name)
Console.WriteLine(lastUpdatedFile)
End Sub
End Module
■No26221に返信(マメオさんの記事)
> サーバーからの取得情報に例で示したもの以外があったのでしょうか?
ファイル一覧の文字列の中に ClosingData: 226 Directory send OK. の文字
列が含まれてしまっているのではないでしょうか。
ただの予想です。
本題はこちらです。LINQ を利用しているところで例外が発生しています。そ
してエラーメッセージは引数がおかしいですよーといった意味ですが、どこの
引数なのかまでは特定できません。問題解決のためには LINQ で行う処理のど
こで例外が発生するのか、さらに絞り込む必要があります。
LINQ で行われる処理を分解して、例外が発生する処理の特定と、そのときの
行文字列がどのようなものか確認してみてはいかがでしょうか。
原因が明確になれば、新たに日付形式を追加したりといった具体的な対策を考
えることが出来ます。
Sub Main()
...省略...
'予期される日付形式
Dim datePattern() As String = {"MMM dd yyyy", "MMM dd HH:mm"}
Dim ci = CultureInfo.InvariantCulture
Dim dts = DateTimeStyles.None
' 各行の44文字以降を切り出す処理
Dim convertedLines = Array.ConvertAll(lines, Function(x) Mid(x, 44))
For Each convertedLine In convertedLines
' 文字列から空白の位置を探す処理
Dim position = InStrRev(convertedLine, " ")
' 文字列からファイル名を切り出す処理
Dim fileName = convertedLine.Substring(position + 1)
' 文字列からファイルの日付を切り出す処理
Dim fileDateString = convertedLine.Substring(0, position - 1)
' 日付を DateTime 型に変換する処理
Dim fileDate = DateTime.ParseExact(fileDateString, datePattern, ci, dts)
Next
End Sub
おはようございます。 >にてエラーが発生しました。 読んだ通りのエラーですので… データに関することは人に聞いても確実な答えは出ませんから、 場所も分かったことですしご自身でデバッグ実行してその際のposition変数やconvertedLine変数の値を 確認されてはいかがでしょう。 (tryで囲んでいる場合、一時的にtryをコメント化すれば、 ステップ実行せずにF5で一気に実行しても例外発生箇所に黄色い行がとどまってくれるので 変数にマウスカーソルをあてればすぐ確認できると思います。) また、一覧のフォーマットが気になったのですが、 マメオさんの示されるデータは二回とも各部のスペースがすべてひとつになっていましたが、 魔界の仮面弁士さんの示されるような桁合わせされたフォーマットにはなっていないのでしょうか。
■No26243に返信(マメオさんの記事) >>・ディレクトリとファイルを区別する必要があるのか、無いのか。 > 区別が必要です。 その場合、行頭文字で判別できると思います。 -rw-r--r-- 1 505 506 3351 Nov 18 17:34 ***.aaa.CSV drwxrwxrwx 2 0 0 69632 Jan 08 21:20 edp おそらく、行頭が "-" ならファイル情報、"d" ならディレクトリです。 そのいずれでも無い行は、解析対象外として扱うようにすれば良いかと。 > 時間で抽出すれば、最新の「ABC」ファイルは一つです。 ここでいう 「ABC ファイル」というのは、 「ファイル名が "ABC" に完全一致するファイル」ではなく、 「ファイル名の一部に "ABC" を含むファイル」の事と 理解しましたが、この認識で間違いないでしょうか? > ファイル名違いを含めれば、最新は一つではない場合もあります。 同一ファイル名が同時に複数現れる事は無いと思いますので、これは 「"ABC" を含むファイル名」が複数登場した場合の事だと読み取りました。 その上で、その ABC ファイル群の中に、更新時刻が秒単位まで完全一致する ファイルがありえる…という意味に受け取りましたが、この認識で合っていますか? >>・空白を含むファイル名があった場合、どのように表示されるのか。 > 空白を含むファイル名はありませんが、あった場合のことは未確認です。 たとえば Windows XP だと、エクスプローラーでコピーしたファイルは、 "test.txt" → "コピー 〜 test.txt" という名前が付けられたりしますよね。 今回の解析法では、最後の空白以降にある文字列をファイル名として 扱っていますので、万一、上記のように空白を含むファイル名があった場合、 正しく解析できない事になります。 >>・西暦部の表示が省略される事があるようだが、万一、FTPサーバーと >> クライアントのシステム日付が1年以上ずれていた場合、更新日を >> 正しく取得できるのか否か。 > サーバーに直接アクセスすると通常の時間表示なので問題ないと思っています。 西暦部が省略されるのは、おそらく「今年更新されたファイル」に対してですよね。 ではその「今年」とは、サーバー日付でしょうか? クライアント日付でしょうか? 恐らくはサーバー日付だと思います。 例えば、"2009/12/31 12:34:45" に更新されたファイルがあったとします。 FTP サーバーの日付が 2009/12/31 23:34:45 の時、取得結果は恐らく、 「Dec 23 12:34」という文字列で返されるものと予想されます。 そしてクライアント時刻が、2010/01/01 00:23:45 にずれていたとします。 その場合、先のロジックで解析すると、「Jan 23 12:34」を日付化した結果、 "2009/12/31 12:34:00" ではなく、"2010/12/31 12:34:00" として 解釈されてしまう恐れがあるのではないでしょうか。 >>・昨年以前のファイルには時刻情報が含まれないようだが、その場合、 >> 日付だけでなく、時刻情報を追加取得する必要があるのではないか。 > 調べ直したところ、指摘の通りでした。 > また古いファイルは西暦表示だけなので、無視して良いかと思ってましたが > 年が変わり2010年になって、西暦情報も必要になっているのに気付きました。 となると、今のファイル一覧からの解析は不可能ですので、 No26117 にて指摘していた対処が必要になるかと思われます。 >>・いままで提示されてきたサンプル群の内容を、自分なりに理解できているか否か。 > 全て理解が出来ているとは言えません。 「全て」とは、理解度0%という意味ではなく、部分的には理解できたという意味ですよね。 分からなかった部分があるなら、その点を具体的に質問していただければ、 何かしらの追加説明をできるかと思います。 >>・エラーが発生した際に、そのエラーメッセージの意味を理解できているか否か。 > それについても上記のとおりです。 では、No26233 のエラーの意味は理解できましたか? > Dim fileName = convertedLine.Substring(position + 1) > 「ArgumentOutOfRangeExpectionはハンドルされませんでした。」 > 「startIndex に文字列の長さより大きい値を指定することはできません。 パラメータ名: startIndex」 このエラーは、ArgumentOutOfRangeExpection という例外です。 文字通り、引数に渡した値が範囲外である事を示しています。 そして、この例外に続くメッセージには、詳細情報として Substring メソッドの引数(startIndex) に指定した「文字位置」が、 元の文字列(convertedLine)の長さに収まっていなかった という事が示されています。 そこまで分かれば、エラー発生時にどのようなデータを指定したのかを 調べることで、「データの問題」なのか「解析処理の実装ミス」なのかを 判断できるかと思います。
分類:[.NET]
いつもこちらのページのお世話になっておりますVB.NET初心者です。
プログラム作成にて問題があり投稿致しました。
目的
あるFTPサーバーに保存されたデータから、決まったファイル名(例***ABC.CSV)
の最新版をダウンロードしたいと思っています。
こちらのサンプルコードを参考にサーバーへの接続及びFtpWebRequestの
Listコマンドを使用しての一覧の取得までは出来たのですが、そこで
躓いてしまいました。
初心者のため状況説明に足りない点もあるかもしれませんが、
どうかご教授お願いします。