FC2ブログ


VS2017-はじめの1/10歩(17):HTTPでファイルを送受信する(2)

   プログラミング [2019/09/14]
VS2017を使うんだけど慣れてないからVS6でまずは作ってました。
さらに、言わばWin32アプリです。
両方の環境(クロス環境)でコンパイルできるソースとして書いてます。
これまでの書き込みも含めると、

→ナレーション再生と音声認識を使う
  →そのため音声データの作り方(多言語対応)
  →ナレーション(.WAV)再生のサンプルコード
  →音声認識のサンプルコード
→VS6で作ったC/C++コードをVS2017に持って行って.cppレベルで共通にする方法
→ダイアログベースのプログラム
  →背景に画像(.BMP)を使う
  →ラベルを透かす
  →ボタンの色を変える
  →Windowsタブレット上でピンチイン/アウト:拡大縮小
  →タブレット画面の回転への対応
  →カスタムなチェックボックス作成
  →ボタンに画像を貼付ける
  →スクロールとスワイプ
→HTTPでファイルを送受信する
  →手法1:ブラウザ使って
  →手法2:Wininetを使う←今回

前回、1週間くらい掛けて調べた挙句に、当初の前提をチャラにして、
ブラウザコントロールは使わずに必要な機能を再度作ることにしました。

つまり、サーバスクリプト側には、
 ・ダウンロード:欲しいデータをリクエストして、
          お返しにそのデータファイルをレスポンスしてもらう。
 ・アップロード:アップしたいデータを伝えて、一緒にファイルも送り込み、
          結果のステータスのみレスポンスしてもらう。

◆ファイルを単純にダウンロードするだけ
URLDownloadToFile()を使ったら、あっさりできた。
しかも、呼び出したプログラムのカレントフォルダに書き込んでくれる。
(前回の「IEイベント操作方法」捜索は、もっと早くにあきらめればよかった。)

ヘッダとかに以下が必要。
//----------------------------------------------
#include <urlmon.h> //URLDownload()
#pragma comment(lib, "urlmon.lib")
//----------------------------------------------


ダイアログとかにダウンロード用のボタンがあるものとして。
ウィンドウプロシジャ内で
//----------------------------------------------
case WM_COMMAND:
if (LOWORD(wParam) == IDC_BTDL1) {
//パターン1:URLDownloadToFile
HRESULT rs;
rs = URLDownloadToFile(g_pWB2, g_FileDLUrl, "tameshite.zip", 0, NULL);
if (rs == S_OK) {
OutputDebugString(_T("Download success!\n"));
}
else if (rs == E_ABORT) { //0x80004004
//URLDownloadToFile()の第3パラメタでパス指定するとアボートされる。
//→IEテンプレートフォルダ?(このプログラムのカレント)に書き込まれる。
OutputDebugString(_T("Download error!! E_ABORT\n"));
}
else {
OutputDebugString(_T("Download error!! unknown\n"));
int rt = GetLastError();
}
}

//----------------------------------------------


URLDownloadToFile()関数の先頭パラメタg_pWB2は、AtlAxGetControl()が使えましたが、NULLでも大丈夫そうです。
g_FileDLUrlは、ダウンロードするファイルのURL、あるいはスクリプトのURLを指定します。


◆ファイルを問答無用でサーバ側に送りつける
ダウンロードで、ブラウザが必要なHTMLのフォームを介してのダウンロードは断念したので、アップロードも問答無用としたい。
調べました。

https://www.experts-exchange.com/questions/21043473/Automating-Internet-Explorer-to-upload-files-with-VC.html ★
HTMLとしてブラウザ上に表示されているformを操作するような感じなのかな?
まずは、キープ。

http://frog.raindrop.jp/knowledge/archives/000287.html
MFCを使う方法の説明。MFCかー。

http://programing-memo.blogspot.com/2011/05/ccgi.html
サーバ側の処理をC++で・・・みたいなので違います。

http://sdlabo.org/index.php?C%E8%A8%80%E8%AA%9E%E3%81%A7POST%E3%81%97%E3%81%A6%E3%83%90%E3%82%A4%E3%83%8A%E3%83%AA%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89 ★
WinSockを使う方法かと思います。シンプルで分かりやすい。
これもキープ。

https://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=34969&forum=7 ▼
VBのWebClient.UploadFileをVCでやりたいけど、上手くいかないとのQ。
Aがない。
でも、WebClientってこれまでも出てきた気がする。
これしか、ないのか?

と、ここで「cURL」というワードが登場。
https://teratail.com/questions/100418
https://curl.haxx.se/libcurl/c/example.html
https://curl.haxx.se/download.html

wikiで説明を読むと、なんか自由に使ってよさげなフリーのライブラリらしく、Windows版もある。
使用方法を説明するサンプルコードもあって、見ると至ってシンプルだ。
ダウンロードもアップロードもある。
ぐっと来てますが、
あれもこれもを追加すると、今はいいけど先々の継続性に不安を抱えることになるわけで。
MS社提供環境に絞って仕方なく使っているのも、いくらかでもOS/VSのバージョンアップ時の「互換」を期待してのこと。
・・・もうちょっと調べよう。

これまで使ってきたヘッダやライブラリをベースにファイルのアップロードについて語っているところを探すために「COM」「IWebBrowser2」とかのキーワードをプラスして探しましたがなかなか、さるが理解できそうなところに当たらない。

飽きちゃって、ぼやっと検索されたサイトを見てたら、
あれ?「Wininet」ってあるのね。
要はブラウザじゃない、URLアクセスですよね。
なんだよ、ちゃんとあるんだね。

ダウンロードのサンプルはすぐ見つかりました。
https://blog.systemjp.net/entry/2016/08/10/110128
http://yamatyuu.net/computer/program/sdk/wininet/wgetbin/index.html

アップも?・・・ありました。
https://code.i-harness.com/ja-jp/q/7309e
https://stackoverflow.com/questions/10097216/c-wininet-file-upload-with-http-post
https://www.dinop.com/vc/wininet_post.html

またもや方針転換です。
URLDownLoad()を使うのは止めて、Wininet APIを使います。


まあともかく、動かせそうなレベルまでコードを書きました。(ほぼパクリですけどね。)

じゃあ、動かして見ましょー・・・




◆サーバスクリプトとともにデバッグする
サーバスクリプト側をどうするかと考えた際
さるな乏しい経験としては、ASP→Perl→PHPとそれぞれ使ったことはある。
一番最近(とは言っても5-6年前)使ったPHPをチョイス。
開発環境は、・・・やっぱり前使ってたEclipseをセレクト。
(※でも、Ver.Upで機能が変わってて初めて使うのとあまり変わりなかった。)

で、作り始めました。

※Eclipseの使い方に関しても、デバッグ設定に相当難儀したのですが、それは別の回で。

EclipseでPHPのコード書いてデバッグを始めます。
いきなりアプリからHTTPリクエストを発行しながらとはいかないので、PHP単独でやりますよね。

Eclipseの「実行構成/デバッグ構成」で「http://localhost/xxxx/xxxx.php?param1=xxx...」とか設定して確認できます。
この場合、パラメタ指定はGETなので、PHPソース内では$_REQUEST["param1"]とかで取り出すようにしておきます。

アプリなしでPOSTメソッドでリクエストしたい場合は、
テスト用に<FORM ... method="post">を仕込んだHTMLを作成して、実行/デバッグ構成にそのHTMLを起動するように指定すれば、発行されたリクエストで作ったPHPのデバッグはできるかと思います。

どういうわけか、EclipseでPHPをデバッグしている際に、クライアントアプリを起動すると、Eclipse側が反応して先頭のブレークポイントで止まるような現象がありました。
何にもしないのにできるのか?と思ったりしたのですが、Eclipseに来るときと来ないときがあって、動作が一定しません。

なのでちょっとの間、error_log(メッセージ)でをログに吐き出してテストしました。
ちなみに、さるの環境では、xampp\php\logs\php_error_log に吐き出されるようになってました。
httpのアクセスログは、xampp\apache\logs\access.log ですね。

が、効率は良くない。
なので、HTTPリクエストを発行して、デバックモードをキックできないのか調べました。

引っかかったのが、こちらのサイト
https://qiita.com/castaneai/items/d5fdf577a348012ed8af

URLにチョイタシしてリクエストしたら・・・できました。

これで、効率は格段に上がる。

とは云うものの
「かなり、いい線行ってるかも」と思ったWininet使用版のコードは最初、全く動きませんでした。
POSTで渡すパラメタの指定方法がいい加減だったせいでした。

特に、デバッグ段階で四苦八苦したのがアップロード側です。
HttpSendRequestEx()を使うサンプルを見つけて参考にしたのですが、
ファイルデータそのものだけを送るようにしか説明してくれていなかった。

今回のように、アクセス認証と機能を選んで一緒に送られてきたファイルデータをサーバーにセーブするというのをどうすればいいかです。

HttpSendRequestEx()はいわば、「これから、こんくらいのデータを送りますよ」宣言だけで、
データ自体は、InternetWriteFile()でセットする。
実際の送信は、HttpEndRequest()時に行われるようです。

通常<form ...>で送られるようなパラメタとその後のデータの中身は、HTTPで実際にどう送られているのかを知らないと作れない感じ。

そんなとき、とても参考になったのが、以下のサイト。
https://www.yoheim.net/blog.php?q=20171201
神様仏様。

でもって、デバッグしてなんとかできたアップロード/ダウンロード用クライアント側処理はこちらからダウンロードできます。
前回説明の紆余曲折も含まれているので、見るのがキツイかもしれませんけど、ご勘弁。

サーバ側PHPも無いと・・・サンプルを確認できませんね。
ざっくりしたコードを載せます。
<?php
//*****************************************************************************
// title: ファイル転送用サンプルコード
// file: phptransfile.php
// note: 指定されたフンクション毎にファイルの送受信を行う
// 1:送信
// 2:受信
// parameter: p1:ID
// p2:ハッシュパスワード
// p3:ファンクション番号
// coder: by sarumosunaru
//*****************************************************************************

define('GET_FILE', 'xxxxxxxx.dat');
define('PUT_FILE', 'yyyyyyyy.dat');

date_default_timezone_set("Asia/Tokyo");

session_start();

$errmsg = "phptransfile: ";

if (!isset($_POST["p1"]) || !isset($_POST["p2"]) || !isset($_POST["p3"])) {
$errmsg .= "in debuging? p1=".filter_input(INPUT_POST,"p1").",p2=".filter_input(INPUT_POST,"p2").",p3=".filter_input(INPUT_POST,"p3")."\n";
error_log($errmsg);
}
else {
$user_id = $_POST["p1"];
$hash_word = $_POST["p2"];
$func_no = $_POST["p3"];
}

//必須パラメタの有無チェック
if (!isset($user_id) || !isset($hash_word) || !isset($func_no)) {
$errmsg .= "invalid parameter.";
$errcd = 403; //403 Forbidden
goto error_exit;
}

//※POSTデータ中の'+'は' 'に置き換わるので戻す。
$hash_word = str_replace(' ', '+', $hash_word);

//****パラメタの有効性チェックとアクセス可否判断
$userinf = array();
$userinf = check_account($user_id, $hash_word); //※※独自に作成してください。
if ($userinf == false || $func_no < 1 || $func_no > 2) {
$errmsg .= "invalid parameter.";
$errmsg .= " p1=".$user_id.",p2=".$hash_word.",p3=".$func_no."\n";
$errcd = 403; //403 Forbidden
goto error_exit;
}

//****ファイル選択(p3)
$fsuccess = false;
switch ($func_no) {
case 1:
$filename = GET_FILE;
$path = "dat/" . $filename;
$direct = 0;
$fsuccess = make_transfile($path, $userinf); //※※独自に作成してください。
break;
case 2:
$filename = PUT_FILE;
$path = "dat/" . $filename;
$direct = 1;
break;
dafault:
goto error_exit;
}

//****ファイルの作成/レスポンス
if ($direct == 0) {
if (!$fsuccess || !file_exists($path)) {
$errmsg .= "not ready to create file.";
$errcd = 503; //503 Service Unavailable
goto error_exit;
}
$size = filesize($path);

//HTTPヘッダ送信
header("Content-Disposition: attachment; filename=" .$filename);
header('Content-Type: application/octet-stream');
header('Content-Transfer-Encoding: binary');
header('Content-Length: '.$size);

//ファイル送信
if (!readfile($path)) {
$errmsg .= "INTERNAL - can not output the data file.";
$errcd = 503; //503 Service Unavailable
goto error_exit;
}
unlink($path);
}
//***受信データの取り込み/保存
else {
//古いファイルを削除する。
if (file_exists($path)) {
if (!unlink($path)) {
$errmsg .= "faile to file delete! file=".$path;
$errcd = 503; //503 Service Unavailable
goto error_exit;
}
}
//ファイルに保存
if (!move_uploaded_file($_FILES['updatefile']['tmp_name'], $path)) {
$errmsg .= "had no upload data.";
$errcd = 421; //421 Misdirected Request
goto error_exit;
}
//受信ファイルへの何らかの処理
//:
//:
}
//****正常終了
exit;

error_exit:
//****異常終了
error_log($errmsg);
http_response_code($errcd);

exit;


//*****************************************************************************
// functuin name :check_account
// note :指定されたアカウントが有効化どうかをチェックする
// parameter :user_id :ユーザID
// hash_word :ハッシュ化パスワード
// return :ユーザ番号
//*****************************************************************************
function check_account($user_id, $hash_word)
{
$userinf = false;
//:
//:
return $userinf;
} //end function

//*****************************************************************************
// functuin name :make_transfile
// note :転送データをファイルに保存する
// parameter :path :セーブ先ファイル名
// userinf :ユーザ情報
// return :true-成功、false-失敗
//*****************************************************************************
function make_transfile($path, $userinf)
{
$fsuccess = false;
//:
//:
return $fsuccess;
} //end function

?>

大急ぎで、へっぽこなコードを書きました。
動作は未確認なのでスンナリ動く保証はありません。

次回、これまたかなり難儀した暗号化のぼやきになります。

では。m(__)m
スポンサーサイト





コメントの投稿

非公開コメント

カレンダー
10 | 2019/11 | 12
- - - - - 1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
プロフィール

さるもすなる

Author:さるもすなる
さるです。別HPサイト「さるもすなる」から侵食してきました。 山菜/きのこ、それとタイトルにしたPPバンド籠のことをメインに徒然に・・・・暇を持て余したさるの手仕事:男手芸のブログってことで。

最新記事
最新コメント
月別アーカイブ
カテゴリ
天気予報

-天気予報コム- -FC2-








本家のHPのトップ
山菜や茸の話です
PPバンドの籠作品と作り方です
投稿をお待ちしております



ブログランキング・にほんブログ村へ にほんブログ村 ハンドメイドブログへ



マニュアルのお申し込み



検索フォーム
リンク
RSSリンクの表示
ブロとも申請フォーム

この人とブロともになる

QRコード
QR