.NET Framework 版の ASP.NET Web API はファイルアップロードの受信に対応してません。正確に言うと multipart/form-data 形式で送信されてきたデータのフォーマッターが実装されていません。
なので、アクションメソッドの引数を Post(HttpPostedFileBase file) というようして、それに multipart/form-data 形式でファイルを送信すると UnsupportedMediaTypeException がスローされ、
"メディアの型 'multipart/form-data' のコンテンツから型 'HttpPostedFileBase' のオブジェクトを読み取るために使用可能な MediaTypeFormatter がありません。"
・・・というエラーになります。
その対処方法を書きます。基本的には Microsoft のドキュメント「ASP.NET Web APIでの HTML フォーム データの送信: ファイルのアップロードとマルチパート MIME」に書いてあった方法です。
例えば以下のような View で HTML5 fetch API を使ってファイルをアップロードするとします。
<form id="form1" method="post" enctype="multipart/form-data">
<input name="caption" type="text" value="Summer Vacation" />
<input type="file" name="file" />
</form>
<button type="button" id="button1" class="btn btn-primary">
アップロード
</button>
<div id="result1"></div>
@section Scripts {
<script type="text/javascript">
let resultDiv, uploadButton;
window.addEventListener('DOMContentLoaded', () => {
uploadButton = document.getElementById("button1");
resultDiv = document.getElementById("result1");
uploadButton.addEventListener('click', uploadFile);
});
const uploadFile = async () => {
let fd = new FormData(document.getElementById("form1"));
const param = {
method: "POST",
body: fd
}
const response = await fetch("/api/FileUpload", param);
if (response.ok) {
const message = await response.text();
resultDiv.innerText = message;
} else {
resultDiv.innerText = "アップロード失敗";
}
};
</script>
}
送信されるデータは以下のように multipart/form-data 形式になります。
------WebKitFormBoundaryoasm5HIwy0wijTSo
Content-Disposition: form-data; name="caption"
Summer Vacation
------WebKitFormBoundaryoasm5HIwy0wijTSo
Content-Disposition: form-data; name="file"; filename="0305Result.jpg"
Content-Type: image/jpeg
それを .NET Framework 版の ASP.NET Web API のアクションメソッドで取得してサーバーのフォルダにファイルとして書き込むコードを書いてみました。以下の通りです。
説明はコメントに書きましたのでそれを見てください。手抜きでスミマセン。
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using System.Threading.Tasks;
using System.Collections.Specialized;
using System.Text;
using System.Net.Http.Headers;
namespace WebAPI.Controllers
{
public class FileUploadController : ApiController
{
// アクションメソッドは引数無しにする。フォーマッタを呼び出さ
// ずアクションメソッド内で要求本文を処理するため
public async Task<HttpResponseMessage> Post()
{
// 要求にマルチパート MIME メッセージが含まれているか確認
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(
HttpStatusCode.UnsupportedMediaType);
}
// アプリケーションルート直下に UploadedFiles というフォル
// ダを設け、そこにアップロードされてきたファイルを書き込
// む。以下のコードでそのフォルダの物理パスを取得
string root = HttpContext.Current.Server
.MapPath("~/UploadedFiles");
// アップロードされたファイルにファイルストリームを割り当て
var provider = new MultipartFormDataStreamProvider(root);
// マルチパート MIME メッセージのすべてのファイル部分を
// 抽出し、上の root で指定されたフォルダに書き込む。
// ファイル名は ReadAsMultipartAsync が自動的に一意に
// なるように生成する
await Request.Content.ReadAsMultipartAsync(provider);
// ReadAsMultipartAsync メソッドが完了すると、FileData
// プロパティで MultipartFileData オブジェクトのコレ
// クションを取得できる。MultipartFileData オブジェクト
// からファイルに関する情報を取得できる
foreach (MultipartFileData file in provider.FileData)
{
// ReadAsMultipartAsync メソッドが自動的に生成した
// ファイル名をフルパスで取得。UploadedFiles フォル
// ダにはその名前でアップロードされてきたファイルが
// 書き込まれている
string source = file.LocalFileName;
// 要求ヘッダに含まれるファイル名を取得。取得した
// 文字列は何故か " で囲われるのでそれを除去
var name = file.Headers.ContentDisposition
.FileName.Replace("\"", "");
string dist = root + "\\" + name;
// ReadAsMultipartAsync メソ��ドが生成したファイル
// 名を要求ヘッダに含まれるファイル名に書き換える。
// 同名のファイルが存在する場合は先に削除する
File.Delete(dist);
File.Move(source, dist);
}
// <input name="caption" type="text" /> で送信されて
// きたデータは以下のようにして取得できる
NameValueCollection col = provider.FormData;
foreach (string s in col.AllKeys)
{
Trace.WriteLine($"Key: {s}, Value: {col[s]}");
}
string result = "アップロード完了";
// .NET Framework 版の Web API は JSON を返すのが基本
// らしく、アクションメソッドの戻り値の型を string にし
// て return result; とすると送信される文字列は " で囲
// われてしまうので以下のようにする
return new HttpResponseMessage
{
Content = new StringContent(result,
Encoding.UTF8,
"text/plain")
};
}
}
}
注: アップロードしたファイルが指定したフォルダに書き込まれたことを確認するには Windows 付属のエクスプローラーを使ってください。Visual Studio のソリューションエクスプローラーでは見えません。理由は不明ですが、ReadAsMultipartAsync メソッドで書き込まれたファイルは見えないようです。
ちなみに、Web API ではなくて MVC5 のコントローラーや ASP.NET Core の Web API には multipart/form-data 形式に対応するフォーマッターが組み込まれているので、上記のようなことをする必要はありません。なので、そちらの方向に進んだ方が正解かもしれません。