ASP.NET Web API でファイルをアップロードする際に、Content-Type の異なるデータを送信したかったので、multipart / form-data を使う方法を試してみました。画像ファイルのバイナリデータとファイル名を送信して、Azure Blob Storage にアップロードするシナリオで実装しました。
クライアント側
Windows ストア アプリ で、FileOpenPicker から選択した画像ファイルをアップロードします。
#MainPage.xaml.cs private async void Button_Click(object sender, RoutedEventArgs e) { var picker = new FileOpenPicker(); picker.ViewMode = PickerViewMode.Thumbnail; picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary; picker.FileTypeFilter.Add(".jpg"); picker.FileTypeFilter.Add(".jpeg"); var storageFile = await picker.PickSingleFileAsync(); if (storageFile != null) { var dialog = new MessageDialog("アップロードする?"); dialog.Commands.Add(new UICommand("OK")); dialog.Commands.Add(new UICommand("Cansel")); var stream = await storageFile.OpenStreamForReadAsync(); var byteArray = new byte[stream.Length]; var result = await dialog.ShowAsync(); if (result.Label == "OK") { var multipart = new MultipartFormDataContent(); stream.Read(byteArray, 0, (int)stream.Length); var fileContent = new ByteArrayContent(byteArray); fileContent.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg"); multipart.Add(fileContent, JsonConvert.SerializeObject("buffer")); var addContent = new StringContent(JsonConvert.SerializeObject("test.jpeg")); addContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); multipart.Add(addContent, JsonConvert.SerializeObject("fileName")); var response = await new HttpClient().PostAsync("http://localhost:23436/api/blob/", multipart); response.EnsureSuccessStatusCode(); await new MessageDialog("成功!").ShowAsync(); } } }
MultipartFormDataContent に ByteArrayContent(画像ファイル)と StringContent(ファイル名)を含めて、HttpClient クラスで POST しています。Add メソッドで指定している名前(buffer と fileName)は、サーバー側で値を取り出すときに使います。
サーバー側
Azure Blob Storage にファイルをアップロードする BlobController を用意します。
#BlobController.cs [HttpPost] public async Task<IHttpActionResult> Upload() { if (Request.Content.IsMimeMultipartContent() == false) { throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); } var provider = await Request.Content.ReadAsMultipartAsync(); var fileContent = provider.Contents.First(x => x.Headers.ContentDisposition.Name == JsonConvert.SerializeObject("buffer")); var buffer = await fileContent.ReadAsByteArrayAsync(); var json = await provider.Contents.First(x => x.Headers.ContentDisposition.Name == JsonConvert.SerializeObject("fileName")).ReadAsStringAsync(); var fileName = JsonConvert.DeserializeObject<string>(json); var blob = this.container.GetBlockBlobReference(fileName); blob.Properties.ContentType = fileContent.Headers.ContentType.MediaType; await blob.UploadFromByteArrayAsync(buffer, 0, buffer.Length); return Ok(); }
ReadAsMultipartAsync メソッドで、マルチパートのコンテンツを読み取るプロバイダークラスを取得します。このプロバイダークラスを使って、クライアント側で指定した名前(buffer と fileName)をキーに、ByteArrayContent(画像ファイル)と StringContent(ファイル名)を取り出すことができます。
まとめ
ASP.NET Web API で Content-Type の異なるデータを送信するには、multipart / form-data を使います。複数のファイルをまとめてアップロードすることも可能です。ちなみに、ファイル名であれば、multipart / form-data ではなく、Content-Disposition ヘッダーの FileName を使う方法もあります。
追記:ASP.NET Core の場合