はじめに
良いタイトルが思いつかなかった。。。
PDFファイルとかを一纏めにしたZIPファイルのバイナリデータをWebサーバから取得して
クライアント端末に保存したい。
ということで、C#(.Net FW 4.5.2)で作成してみます。
システム構成図
- ConsoleAppからWeb I/Fを叩く
- Web I/FがAPIをコールしてZIPのバイナリデータを作成する
- Web I/FがAPIで作成したバイナリデータをConsoleAppに返却する
- ConsoleAppがバイナリデータをクライアント端末に保存する
という処理の流れになります。
コンソールアプリケーション
まずはクライアント端末に配置するコンソールアプリケーションから。
using System;
using System.Xml;
using System.Web;
using System.Net.Http;
using System.IO;
using System.Configuration;
using System.Text;
using System.Threading.Tasks;
namespace DownloadApp
{
class DownloadApp
{
/// <summary>
/// メイン処理
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
try
{
DownloadZipAsync().Wait();
}
catch (Exception ex)
{
// エラー処理
}
}
static async Task DownloadZipAsync()
{
using (var client = new HttpClient())
{
// 1. GETでデータ取得
string uri = "http://localhost:50984/WebService/WebService.asmx/GetBinaryOfZip?userId=1";
HttpResponseMessage res = await client.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead);
// 2. 保存するzipファイルのストリームをオープン
string outputFilePath = "C:\temp\test.zip"
using (var fs = new FileStream(@outputFilePath, FileMode.Create, FileAccess.Write))
{
// 3. 受信データの中身を読み込む
var resXml = await res.Content.ReadAsStringAsync();
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(resXml);
if (xmlDoc.GetElementsByTagName("base64Binary").Count == 0)
{
throw new ApplicationException("base64Binaryタグが存在しません。");
}
// 4. Webサービスから取得したBase64文字列をバイト型配列に変換
byte[] zipBinary =
System.Convert.FromBase64String(xmlDoc.GetElementsByTagName("base64Binary")[0].InnerText);
// 5. ファイルに書き込む
await fs.WriteAsync(zipBinary, 0, zipBinary.Length);
}
}
}
}
}
やっていることはシンプルです。
コード上の番号付きコメントを見れば大方わかると思います。
GETのURLの中身はテキトーです。環境に合わせて変更が必要です。
いくつかポイントを解説します。
・DownloadZipAsync().Wait()について
なぜWaitしているのか?
と思われた方がいらっしゃると思いますが、
コンソールアプリケーションで非同期メソッドを使用する場合、
Waitで処理の終了を待ってあげないと一瞬でMainの処理が終了してしまい、期待通りの動作になりません。
※今回はGETでWeb I/Fを呼び出す方法として「HttpClient」を使用しているため、DownloadZipAsyncメソッドは非同期メソッドとなっています。
・System.Convert.FromBase64Stringについて
今回のサンプルで呼び出すWeb I/Fは「asmx.cs」に定義されているメソッドになります。
ここからバイナリデータを返却するとbase64でエンコードされるので、
クライアントで受け取った際にbase64でデコードしてやる必要があります。
※特に意識せずとも自動的にbase64でエンコードされて返却されるようなのですが、こういう仕様なのでしょうか?わかる方いらっしゃいましたらご教授いただけると幸いです。
Web I/F
次にコンソールアプリケーションからコールされるWeb I/F。
[WebMethod]
public byte[] GetBinaryOfZip(string userId)
{
return GetBinary(userId);
}
private byte[] GetBinary(string userId)
{
using (var ms = new System.IO.MemoryStream())
{
// メモリストリーム上にZipArchiveを作成する
using (var zipArchive = new System.IO.Compression.ZipArchive(ms, ZipArchiveMode.Create, true))
{
string entryName = userId + "_test.jpg";
byte[] buffer = [jpgのバイナリデータ];
// ファイル名を指定してエントリを作成
System.IO.Compression.ZipArchiveEntry entry = zipArchive.CreateEntry(entryName);
using (Stream es = entry.Open())
{
// エントリにバイナリを書き込む
es.Write(buffer, 0, buffer.Length);
}
}
return ms.ToArray();
}
}
すみません、説明簡略化のためにAPIの処理もここに含めてしまっています。
本来privateのGetBinaryメソッドはここにいるべきではありません!
ビジネスロジックをWebの顔に書くのはやめましょう。
では、こちらもポイントを解説します。
・ZipArchiveについて
これ非常に便利です。
ZipArchiveとZipArchiveEntryを絵で説明するとこんな感じです。
ZipArchiveという箱(要するにzipファイル)の中に
ZipArchiveEntryという物(要するに画像やPDFのファイル)を
入れ込んでいるといったイメージです。
このコード例ではエントリを1つしか書き込んでいませんが、
複数書き込むことで複数のファイルをzipファイルの中に入れることができます。
おわりに
備忘録代わりにと思ってだいぶ駆け足で書いてしまいました。
それでも誰かの参考になれば幸いです。