とあるサーバーへ定期的にファイルをアップロードする必要があります。
指定のページ(recieve.asp)にてローカルのファイル名を入力して
submitボタンで実行します。
この処理を自動化するためにaccess2002 vbを利用します。
Set xmlhttp = CreateObject("MSXML2.XMLHTTP")
xmlhttp.Open "POST", "http://---/recieve.asp", False
xmlhttp.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
(xmlhttp.setRequestHeader "Content-Type", "multipart/form-data")
strSend = "Upload=" & 送信ファイルフルパス
xmlhttp.send (strSend)
ここの最後の行 xmlhttp.sendで必ずハングします。
RequestHeaderはapplication...でも、multipart..でも同じでした
「ファイルを送信するコーディング」というよりは
ブラウザの指定のインプット項目=Uploadにファイル名(文字)を
入力して、submitすることを自動化したいのですが
よいヒントがありますでしょうか。
文字だけの場合との違いは、requestheaderとinput file
とは思いますが、、
元ページ一部
<form method="POST" enctype="multipart/form-data" action="recieve.asp">
<input type="file" name="Upload" size="50">
<input type="submit" value=" アップロード ">
</form>
私がやった方法では・・・
FTPで認証させて、該当ファイルをUPLOADし、後、HTTPで
サーバーサイドスクリプトを『http://----/move.asp』と
かしてキックし、FTPでアップしたファイルをサーバー側
で所定のドライブ・パス等に移動・加工を行う・・・
と言う方法で実現させました。
気をついけないといけないことは、やはりサーバーサイド
でのその処理を行うプロセスの権限と、HTTP側で楽に動か
せるようにすると言う部分で、\InetPub\wwwroot\xxxx の
パスの部分を、FTP側では仮想ディレクトリに設定して、
FTPでアップ時に、HTTP側からすぐに見えるフォルダに、
該当のファイルをアップする・・・
と言うことでした。
ASPでもASP.NETでもどちらでも簡単に書けます。
私はクライアント側はVB.NETで実装して、サーバー側は
ASPで行いました。サーバーにASP.NETを新たにインスト
ールするのは避けたかったもので・・・
またHTTPでのファイルのアップに関してはいろいろな制
限や問題がある・・・と考えた為、セキュリティーも考
慮し、どのみちファイルをアップするというプロトコル
を経由するので・・・と言う意味でFTPにしました。
匿名は拒否し、必ず認証を行うことにしました。
ファイルサイズやアップの頻度では、FTPの方がCPU的に
負荷が大きい場合があります。ファイルの大きさや頻度
の前提条件が解らないので、あくまで、簡単に行える方
法として・・・と言うことで。
ご参考までに・・・
以上。
早速のレス、大変感謝いたします。
前提条件としては、「サーバー側は、こちらで一切いじれない」と
いうことです。(取引先のものなので、、、)
つまり、こちらのファイルがどこにどんな名前で
転送されるかということが一切わからないわけです
となると「ブラウザを介して行う手作業」を自動化
したいと思ってます
> ブラウザの指定のインプット項目=Uploadにファイル名(文字)を
> 入力して、submitすることを自動化したいのですが
その方法(RFC1867)以外で送信しても、問題ありませんか?
<input type=file>の方式で送信しても良いのですが、それをXMLHTTPで行おうとすると、エンコードが少々面倒かと思いますよ。
例えば、ASP側で BASP21.DLLのFormSaveAsメソッド等を利用されている場合は、Content-Typeヘッダでmultipart/form-dataを指定し、かつ、xmlHttp.send に渡すデータを、エンコード処理しておく必要がありますよね。
しかし例えば、送り側を
Dim B() As Byte
B = ファイルの生データ
xmlHttp.send CVar(B)
のようにして、ASP側を
Set X = Server.CreateObject("ADODB.Stream")
X.Open
X.Type = 1 'バイナリモード(adTypeBinary )
If Request.TotalBytes = 0 Then
X.Write Request.BinaryRead(Request.TotalBytes)
X.Position = 0
X.SaveToFile strServerHDDPath, 2 'adSaveCreateOverWrite
End If
のように、Stream.SaveToFileで保存するような場合は、Content-Typeヘッダの指定は不要となります。
この方式の場合、ファイルのバイナリをそのまま渡せるので、エンコード処理も不要ですし、送信するデータ量も小さくできますよ。
> ここの最後の行 xmlhttp.sendで必ずハングします。
XMLHTTPの同期モードで送信されていますよね。
ためしに、非同期モードでもハングするかどうかを確認してみてください。
データ量が多い場合、サーバとの送受信に時間がかかりますので、通信が完了するまで、送り側のプログラムは凍ったように見えるかと思います。
もし、データ量が多いようであれば、必要に応じて、分割してアップロードする事も検討してみては如何でしょうか。
あるいは、他のアップロード手法…例えば、FTPとかWebDAVとか…を利用して見るのも手かも知れませんよ。(IISなら、WebDAVもサポートされていますしね)
--------
ちなみに私も、xmlHttp.send でアップロードするシステムを、何度か作っています。(^^;
その場合、サーバ側に正しくアップロードされたかを確認するために、ASP側で保存処理終了後に、そのファイルのMD5値を調べ(BASP21.DLLを利用)、その値を Response.AddHeader メソッドで送り返すようにしています。
クライアント側は xmlHttp.sendの完了後に、xmlHttp.getResponseHeader にてそのMD5値を受け取り、送信したファイルのMD5値と比較して、同じバイナリになっているかどうかを比較する…という処理にしています。
おっと、すれ違い。
> 前提条件としては、「サーバー側は、こちらで一切いじれない」と
> いうことです。(取引先のものなので、、、)
そういう事でしたか。
とすると、multipart/form-data 形式で送信するしか無いですね…。
指しあたって、開発環境に InetSpy(あるいは、それに類するツール)をインストールしてみる事をお奨めします。
InetSpy を起動しておくと、ブラウザでの通信内容を見ることができるようになりますので、<input type=file>でsubmitした時のデータと、ご自身で作成された XMLHTTP による送信データに食い違いが無いかどうかを確認してみては如何でしょう。
[InetSpy]
http://hide.maruo.co.jp/software/inetspy.html
# なお、InetSpyで参照できるのは http なURLのみとなります。
# SSL(https) なサイトの場合には、データの閲覧はできません。
本当にありがとうございます
とりあえず途中経過報告です。
非同期モードの時は、ハングしないようです。
また、inetspyで見たときは、送信内容にかなりの食い違いが^^;
おそらくはエンコード部分を実装しないといけないようですね
ちょっとやってみます。
(以下脱線)
ちなみに、この辺をまとめてある書籍などご存じないですか?
仮面弁士様は、ひとかどのお方のように見受けられますが
なにか執筆しているものなどあれば、購入することで
お礼にもなりますし、、、(^_^.)
> おそらくはエンコード部分を実装しないといけないようですね
それは必須ですね。
インターネットメディアタイプ multipart/form-data の場合、
バイナリデータをテキストにエンコードしてから送信する必要があります。
「RFC 1867」などの資料を参照してみてください。
> ちなみに、この辺をまとめてある書籍などご存じないですか?
まとめてある書籍は知りませんが、サーバとの通信内容に関して
知りたいのであれば、RFCを参照するのが確実かと思います。
また、XMLHTTPに関しては、MSXML SDKで知ることができます。
(どちらも、英語で書かれているのが難点ですが…)
> ひとかどのお方のように見受けられますが
一応、Visual Basic分野の「MSMVP」だったりします。
# といっても、現在は年度更新の時期なので、来期以降も
# MSMVPでいられるかどうかは未定ですけれども。
> なにか執筆しているものなどあれば、
執筆はおろか、blogさえ書いていません。。。(^^;
引き続き、、、
定型業務なので、毎回ほぼ決まった処理です
そうすると、一番手っ取り早い方法は
inetspyで見られるブラウザ経由の文字列と
msxml経由の文字列をまったく同じにしてしまえばいいと思いますが
合ってますでしょうか。
とりあえずsetRequestHeaderに必要な行を追加したのと
「Refererまでも、、(~~;)」
sendコマンドで送る文字列を、
文字コード変換、boudry行追加、改行コード付加して送っていますが、
漢字の変換がうまくいきません。
単純にunicodeからutf8へ変換をかけるだけではダメなのでしょうか
> 漢字の変換がうまくいきません。
実際にエンコード処理などを作りこむのは後回しにして、とにかく、
IEと同一のヘッダ/ボディ情報を送信した時に、同じ結果が
サーバから得られるかを確認するのが先かと思いますよ。
> 単純にunicodeからutf8へ変換をかけるだけではダメなのでしょうか
…UTF-8もUnicodeなのでは。
# ちなみに、VB6が使っているUnicodeは「UCS-2」です。
はい、ご指摘ありがとうございます。
文字列を調べたところ、相手側はsjisのようでした。
少なくともinetspyの画面を見た限りでは
漢字までブラウザ経由との一致を見てます。
改行コードに不安がありますが、、、
自分の環境で、ファイル受け入れ用aspページだけ作ってみましたが
こちらでは、結果の内容はともかく、
20秒ほど経過するとinetspyのレスポンスと
同じタイミングで、msxml(access)に処理が戻るようです。
ところが、本番環境では、数ミリ秒でinetspyにレスポンス(結果)が
表示されるのにもかかわらず、msxml(access)はハングします。
inetspyの中では「正常に処理されました」と表示
ちなみに非同期モードでは、ハングはしませんが、
responsebodyが帰ってこないところをみると
事実上はハングなのかなと思います。
msxmlは正確には、MSXML2.XMLHTTPです
> 改行コードに不安がありますが、、、
なるほど、InetSpy側だと受信側は「中身をファイルに保存」機能を使って
バイナリでの確認を行えますが、送信側はその機能がありませんからね…。
とりあえず、境界行などの改行コードはCrLfで指定してください。
不安が残るようならば、ASPにて
『Request.BinaryRead(Request.TotalBytes)』や
『Request.ServerVariables("ALL_HTTP")』などを
出力するようなサイトを作ってみれば、IEの動作を確認できますよね。
> 漢字までブラウザ経由との一致を見てます。
ファイル名に漢字が含まれている、という意味なのか、それとも、
ファイルの内容に漢字が含まれている、という事なのかはわかりませんが、
「漢字を含まないファイル」を送信した時は、正しく送れるという事でしょうか?
> msxml(access)はハングします。
う〜ん。通信内容を見てみないと分かりませんが、もしかして、
Content-Length が間違った値を返しているとか…。
> msxmlは正確には、MSXML2.XMLHTTPです
「CreateObject("MSXML2.XMLHTTP.4.0")」を使っても、結果は一緒ですか?
あるいは、XMLHTTPの替わりに、ServerXMLHTTPを使って見た場合はどうでしょう。
なお、ServerXMLHTTPとXMLHTTPの違いに付いては、下記を参照してください。
http://support.microsoft.com/default.aspx?scid=kb;ja;290761
仮面弁士様、ならびに岡田様、本当にお世話になりました
とりあえず固定文字列での送信は成功したので
報告さえていただきます。
解決方法は、
(1)オブジェクトをMSXML2.XMLHTTP.4.0に
変えた時点でハングしなくなりました。
(2)ブラウザ経由からの出力情報を、inetspyで参照し
ヘッダ情報をsetRequestHeaderで追加しました
こちらから送るヘッダ情報順と、サーバー側aspで受け取る順番(行順)は
一致してませんが問題無いようです。
(3)漢字はサーバー側でsjisということがわかりましたので
sjisに変換してから送信
(4)boundary文字列はブラウザからの成功例を元に固定数値
(5)ファイル名とその中身を送信している訳ではなく、
ファイル名もファイル内容も独立した文字列
で、これから送信部分をできることろまで
汎用的なものにしたいのですが、最後の質問です。
ブラウザからmultipart/form-dataを送るときに
(1)boundary文字列はクライアントブラウザから生成されるようですが
msxml経由の時はどうなのでしょうか、
プログラムで作成しなければいけないのでしょうか、
固定のままでは問題ないでしょうか。
(2)ファイル送信については、
ブラウザ経由の時はファイル名を指定すればその中身はブラウザ側で
オープンしてくれますが、msxmlの時は、ファイル名とファイル中身は
こちらで別々に作成しないといけないのでしょうか。
以上です。
そもそも現在の開発環境がaccess2002+office devloperです。
ドキュメントがなさすぎるので苦労してますm(__)m
マルチパート(multipart)タイプとは、複数の添付データをまとめた
メディアタイプとなっています。
InetSpyの結果からも、何となく予想できるかと思いますが、
各データパートの境界は、バウンダリ(境界:boundary)と呼ばれる
『任意の文字列』の行で示されています。
で、この『任意の文字列』は、Content-Type の boundaryパラメータで、
あらかじめヘッダに示されており、送信するデータの各パートは、
『--バウンダリ文字列』(とCRLF改行)で示される事になっています。
そして、データの最後は『--バウンダリ文字列--』で締め括られます。
(この仕組みは、メールヘッダであっても、HTTPリクエストヘッダでも同じです)
さて、質問(1)について…。
> プログラムで作成しなければいけないのでしょうか、
バウンダリは「任意の文字列」ですので、固定でも動的生成でも構いません。
(任意といっても、「1〜70文字で、空白で終わってはならない」などの規則はありますが)
ただし、boundaryパラメタで示された境界文字列と同じ文字列が、
送信データ(マルチパートオブジェクト内の本体部分)に、
含まれていてはならない、という仕様がある事には注意してください。
固定的な文字列を使っても構いませんが、いずれにしても、送信データ中に
バウンダリ文字列が含まれていない事を確認する必要があります。
このあたりの仕様に関しては、後述の資料(RFC 2046)を参照してみてください。
質問(2)について…。
> msxmlの時は、ファイル名とファイル中身は
> こちらで別々に作成しないといけないのでしょうか。
その必要があります。
なお、XMLHTTP.sendに渡す引数には、
・内部形式がStringのVariant型
・内部形式がByte配列のVariant型
・IStreamインターフェイスを持ったオブジェクト
などを指定できます。
> ドキュメントがなさすぎるので苦労してますm(__)m
MSXMLの仕様に関しては、MSXML SDKに解説がありますので、
Microsoftから出されているSDKを参照してください。
また、実際に送信すべき内容に関しては、以下のRFCを参照してください。
<input type=file>による送信に関して: RFC 1867
[RFC1867] Form-based File Upload in HTML
http://www.ietf.org/rfc/rfc1867.txt
RFC1867の参考訳
http://www.studyinghttp.net/rfc_ja/rfc1867_ja.html
メッセージ・ヘッダについて:RFC 2046 (RFC 1522の改訂版です)
[RFC2046] MIME Part Two: Media Types
http://www.ietf.org/rfc/rfc2046.txt
RFC2046の参考訳
http://www.asahi-net.or.jp/~bd9y-ktu/dtd_f/rfc_f/rfc2046j.html
RFC2046 Appendix A の参考訳(バウンダリ文字列の規則が書かれています)
http://www.y-adagio.com/public/standards/tr_mime-p2_2046/mime2046anxa.htm
ついでに……
メッセージ本文について:RFC 2045 (RFC 1521の改定版です)
[RFC2045] MIME Part One: Format of Internet Message Bodies
http://www.ietf.org/rfc/rfc2045.txt
RFC2045の参考訳
http://www.asahi-net.or.jp/~bd9y-ktu/dtd_f/rfc_f/rfc2045j.html