ファイルPOSTを受け入れる方法


254

私はasp.net mvc 4 webapiベータ版を使用してRESTサービスを構築しています。クライアントアプリケーションからPOSTされた画像/ファイルを受け入れることができる必要があります。これはwebapiを使用して可能ですか?以下は、私が現在使用しているアクションです。これがどのように機能するかの例を知っている人はいますか?

[HttpPost]
public string ProfileImagePost(HttpPostedFile profileImage)
{
    string[] extensions = { ".jpg", ".jpeg", ".gif", ".bmp", ".png" };
    if (!extensions.Any(x => x.Equals(Path.GetExtension(profileImage.FileName.ToLower()), StringComparison.OrdinalIgnoreCase)))
    {
        throw new HttpResponseException("Invalid file type.", HttpStatusCode.BadRequest);
    }

    // Other code goes here

    return "/path/to/image.png";
}

3
これは、WebAPIフレームワークではなくMVCでのみ機能します。
Phil

あなたはただアイテムをつかむことができるはずですRequest.Files
Tejs

7
ApiControllerには、Filesプロパティを持つHttpRequestBaseが含まれていません。そのRequestオブジェクトはHttpRequestMessageクラスに基づいています。
フィル

回答:


168

http://www.asp.net/web-api/overview/formats-and-model-binding/html-forms-and-multipart-mime#multipartmimeを参照してください。この記事では、それは本当にです。

基本的に、

public Task<HttpResponseMessage> PostFile() 
{ 
    HttpRequestMessage request = this.Request; 
    if (!request.Content.IsMimeMultipartContent()) 
    { 
        throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); 
    } 

    string root = System.Web.HttpContext.Current.Server.MapPath("~/App_Data/uploads"); 
    var provider = new MultipartFormDataStreamProvider(root); 

    var task = request.Content.ReadAsMultipartAsync(provider). 
        ContinueWith<HttpResponseMessage>(o => 
    { 

        string file1 = provider.BodyPartFileNames.First().Value;
        // this is the file name on the server where the file was saved 

        return new HttpResponseMessage() 
        { 
            Content = new StringContent("File uploaded.") 
        }; 
    } 
    ); 
    return task; 
} 

5
タスクを使用して1つのファイルのみを読み取る利点は何ですか?本物の質問です。タスクを使い始めたばかりです。私の現在の理解から、このコードは複数のファイルを正しくアップロードするときに本当に適していますか?
クリス

48
MultipartFormDataStreamProviderには、BodyPartFileNamesプロパティがありません(WebApi RTM)。参照してくださいasp.net/web-api/overview/working-with-http/...
モズ

5
皆さん、HttpContext.Current.Request.Filesを使用して単純にファイルにアクセスできず、代わりにこの派手なMultipartFormDataStreamProviderを使用する必要がある理由について少し説明してもらえますか?完全な質問:stackoverflow.com/questions/17967544
niaher

7
ファイルはBodyPart_8b77040b-354b-464c-bc15-b3591f98f30fとして保存されています。それらはクライアント上にあるとおりにpic.jpgのように保存されるべきではありませんか?
lbrahim 2014

10
MultipartFormDataStreamProviderBodyPartFileNamesもうプロパティを公開しないので、FileData.First().LocalFileName代わりに使用しました。
Chtiwi Malek 2015

374

多くの人がサーバーにファイルを保存したいと思っているようです。すべてをメモリに保持するための解決策は次のとおりです。

[HttpPost("api/upload")]
public async Task<IHttpActionResult> Upload()
{
    if (!Request.Content.IsMimeMultipartContent())
        throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); 

    var provider = new MultipartMemoryStreamProvider();
    await Request.Content.ReadAsMultipartAsync(provider);
    foreach (var file in provider.Contents)
    {
        var filename = file.Headers.ContentDisposition.FileName.Trim('\"');
        var buffer = await file.ReadAsByteArrayAsync();
        //Do whatever you want with filename and its binary data.
    }

    return Ok();
}

34
ディスク領域を使いたくない場合は、ファイルをメモリに保持しておくと便利です。ただし、大きなファイルのアップロードを許可した場合、それらをメモリに保持することは、Webサーバーが大量のメモリを消費することを意味し、他のリクエストのために何かを維持するために費やすことはできません。これにより、高負荷で動作するサーバーで問題が発生します。
Willem Meints、2013年

21
@ W.Meintsデータを保存したい理由は理解していますが、アップロードしたデータをサーバーのディスク領域に保存したいと思う理由がわかりません。小規模なプロジェクトであっても、ファイルストレージは常にWebサーバーから分離しておく必要があります。
Gleno 2013年

1
投稿されたファイルのサイズが64k未満であることを確認してください。デフォルトの動作はリクエストを無視することです。それ以外の場合は、ログ時間の間、このままです。
Gary Davies 14

3
残念ながら、フォームデータも読み取りたい場合、MultipartMemoryStreamProviderは役に立ちません。MultipartFormDataMemoryStreamProviderのようなものを作成したかったのですが、aspnetwebstackの内部に非常に多くのクラスとヘルパークラスがあります:(
martinoss

9
File.WriteAllBytes(filename, buffer);それをファイルに書き込む方法
2016年

118

この記事を基にした、以下のコードを参照してください。これは、私が見つけた最も単純なコード例を示しています。ファイルとメモリ(高速)の両方のアップロードが含まれます。

public HttpResponseMessage Post()
{
    var httpRequest = HttpContext.Current.Request;
    if (httpRequest.Files.Count < 1)
    {
        return Request.CreateResponse(HttpStatusCode.BadRequest);
    }

    foreach(string file in httpRequest.Files)
    {
        var postedFile = httpRequest.Files[file];
        var filePath = HttpContext.Current.Server.MapPath("~/" + postedFile.FileName);
        postedFile.SaveAs(filePath);
        // NOTE: To store in memory use postedFile.InputStream
    }

    return Request.CreateResponse(HttpStatusCode.Created);
}

26
セルフホスティングコンテナーであるOWINでWebAPIがホストされている場合、HttpContext.Currentはnullです。
ザック、

1
そのように修正しました:var httpRequest = System.Web.HttpContext.Current.Request;
msysmilu 2014年

7
絶対に必要でない限り、WebAPIでSystem.Webを使用しないでください。
Kugel

3
確かに、System.WebはIISに密接に結合されています。OWIN piple lineまたは.Net Core内で作業している場合、これらのAPIはLinuxまたはセルフホストで実行している場合は使用できません。
Kugel 2016年

2
すばらしい答えです。詳細は1つだけです。HTMLページからアップロードする場合、<input type = "file" />タグに「name」属性が必要です。そうしないと、ファイルがHttpContext.Current.Request.Filesに存在しません。
GBU

17

ASP.NET Coreの方法は次のとおりです

[HttpPost("UploadFiles")]
public async Task<IActionResult> Post(List<IFormFile> files)
{
    long size = files.Sum(f => f.Length);

    // full path to file in temp location
    var filePath = Path.GetTempFileName();

    foreach (var formFile in files)
    {
        if (formFile.Length > 0)
        {
            using (var stream = new FileStream(filePath, FileMode.Create))
            {
                await formFile.CopyToAsync(stream);
            }
        }
    }

    // process uploaded files
    // Don't rely on or trust the FileName property without validation.

    return Ok(new { count = files.Count, size, filePath});
}

16

以下は、HTTPボディからアップロードされたファイルのコンテンツを取得してファイルに書き込む迅速でダーティなソリューションです。ファイルのアップロード用に「ベアボーン」HTML / JSスニペットを含めました。

Web APIメソッド:

[Route("api/myfileupload")]        
[HttpPost]
public string MyFileUpload()
{
    var request = HttpContext.Current.Request;
    var filePath = "C:\\temp\\" + request.Headers["filename"];
    using (var fs = new System.IO.FileStream(filePath, System.IO.FileMode.Create))
    {
        request.InputStream.CopyTo(fs);
    }
    return "uploaded";
}

HTMLファイルのアップロード:

<form>
    <input type="file" id="myfile"/>  
    <input type="button" onclick="uploadFile();" value="Upload" />
</form>
<script type="text/javascript">
    function uploadFile() {        
        var xhr = new XMLHttpRequest();                 
        var file = document.getElementById('myfile').files[0];
        xhr.open("POST", "api/myfileupload");
        xhr.setRequestHeader("filename", file.name);
        xhr.send(file);
    }
</script>

ただし、これは「通常の」マルチパートフォームのアップロードでは機能しないことに注意してください。
トム

3
@トムそれはどういう意味ですか?
Chazt3n 2016

これは、JavaScriptが無効になっているか存在しないブラウザー(Netscape 1. *など)と互換性がないことを意味します。
MikaelDúiBolinder

13

私はwebapi mvc4プロジェクトのすべてのNuGetを更新する前に、Mike Wassonの回答を使用しました。実行したら、ファイルアップロードアクションを書き直す必要がありました。

    public Task<HttpResponseMessage> Upload(int id)
    {
        HttpRequestMessage request = this.Request;
        if (!request.Content.IsMimeMultipartContent())
        {
            throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.UnsupportedMediaType));
        }

        string root = System.Web.HttpContext.Current.Server.MapPath("~/App_Data/uploads");
        var provider = new MultipartFormDataStreamProvider(root);

        var task = request.Content.ReadAsMultipartAsync(provider).
            ContinueWith<HttpResponseMessage>(o =>
            {
                FileInfo finfo = new FileInfo(provider.FileData.First().LocalFileName);

                string guid = Guid.NewGuid().ToString();

                File.Move(finfo.FullName, Path.Combine(root, guid + "_" + provider.FileData.First().Headers.ContentDisposition.FileName.Replace("\"", "")));

                return new HttpResponseMessage()
                {
                    Content = new StringContent("File uploaded.")
                };
            }
        );
        return task;
    }

どうやら、BodyPartFileNamesはMultipartFormDataStreamProvider内で使用できなくなりました。


WebApi RTMでは、BodyPartFileNamesがFileDataに変更されました。asp.net/web-api/overview/working-with-http/…で
Mark van Straten

なぜSystem.Web.HttpContext.Current.Request.Filesコレクションを使用しないのですか?
ADOConnection 2013年

私はあなたの方法を2つの予約で使用することを考えています:1)これは2回書きません:i)in ReadAsMultipartAsyncとii)In File.Move?2)できますかasync File.Move
seebiscuit 2017年

1)2回の書き込みで問題がなかったのですが、URLが2回呼び出されていますか?2)Task.Run(()=> {File.Move(src、dest);});を実行できます。
スティーブ・ストークス

10

この同じ方向に向けて、WebApi、c#4を使用してExcelファイルを送信するクライアントおよびサーバーのスニペットを投稿しています。

public static void SetFile(String serviceUrl, byte[] fileArray, String fileName)
{
    try
    {
        using (var client = new HttpClient())
        {
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                using (var content = new MultipartFormDataContent())
                {
                    var fileContent = new ByteArrayContent(fileArray);//(System.IO.File.ReadAllBytes(fileName));
                    fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
                    {
                        FileName = fileName
                    };
                    content.Add(fileContent);
                    var result = client.PostAsync(serviceUrl, content).Result;
                }
        }
    }
    catch (Exception e)
    {
        //Log the exception
    }
}

そしてサーバーのwebapiコントローラー:

public Task<IEnumerable<string>> Post()
{
    if (Request.Content.IsMimeMultipartContent())
    {
        string fullPath = HttpContext.Current.Server.MapPath("~/uploads");
        MyMultipartFormDataStreamProvider streamProvider = new MyMultipartFormDataStreamProvider(fullPath);
        var task = Request.Content.ReadAsMultipartAsync(streamProvider).ContinueWith(t =>
        {
            if (t.IsFaulted || t.IsCanceled)
                    throw new HttpResponseException(HttpStatusCode.InternalServerError);

            var fileInfo = streamProvider.FileData.Select(i =>
            {
                var info = new FileInfo(i.LocalFileName);
                return "File uploaded as " + info.FullName + " (" + info.Length + ")";
            });
            return fileInfo;

        });
        return task;
    }
    else
    {
        throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "Invalid Request!"));
    }
}

そして、ファイル名をカスタマイズするために必要なカスタムMyMultipartFormDataStreamProvider:

PS:このコードは別の投稿http://www.codeguru.com/csharp/.net/uploading-files-asynchronously-using-asp.net-web-apiから取得しました.htm

public class MyMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
{
    public MyMultipartFormDataStreamProvider(string path)
        : base(path)
    {

    }

    public override string GetLocalFileName(System.Net.Http.Headers.HttpContentHeaders headers)
    {
        string fileName;
        if (!string.IsNullOrWhiteSpace(headers.ContentDisposition.FileName))
        {
            fileName = headers.ContentDisposition.FileName;
        }
        else
        {
            fileName = Guid.NewGuid().ToString() + ".data";
        }
        return fileName.Replace("\"", string.Empty);
    }
}

あなたstatic method SetFileはあなたのコントローラーでどのようにあなたを呼ぶのですか?

これは良い答えです。このようにベースプロバイダーを拡張すると、ストリームを制御できるようになり、pathクラウドストレージだけを提供するよりも柔軟性が高まります。
Phil Cooper

6
[HttpPost]
public JsonResult PostImage(HttpPostedFileBase file)
{
    try
    {
        if (file != null && file.ContentLength > 0 && file.ContentLength<=10485760)
        {
            var fileName = Path.GetFileName(file.FileName);                                        

            var path = Path.Combine(Server.MapPath("~/") + "HisloImages" + "\\", fileName);

            file.SaveAs(path);
            #region MyRegion
            ////save imag in Db
            //using (MemoryStream ms = new MemoryStream())
            //{
            //    file.InputStream.CopyTo(ms);
            //    byte[] array = ms.GetBuffer();
            //} 
            #endregion
            return Json(JsonResponseFactory.SuccessResponse("Status:0 ,Message: OK"), JsonRequestBehavior.AllowGet);
        }
        else
        {
            return Json(JsonResponseFactory.ErrorResponse("Status:1 , Message: Upload Again and File Size Should be Less Than 10MB"), JsonRequestBehavior.AllowGet);
        }
    }
    catch (Exception ex)
    {

        return Json(JsonResponseFactory.ErrorResponse(ex.Message), JsonRequestBehavior.AllowGet);

    }
}

6
ユーザーには説明が必要だと思います...!
kamesh 14年

4

ファイルを受け入れる方法は2つあります。1つはメモリプロバイダーMultipartMemoryStreamProviderで使用し、もう1つはディスクに保存するMultipartFormDataStreamProviderを使用します。これは、一度に1つのファイルをアップロードする場合にのみ注意してください。これを確実に拡張して、複数のファイルを保存できます。2番目のアプローチは、大きなファイルをサポートできます。200MB以上のファイルをテストしたところ、問題なく動作しました。メモリ内アプローチを使用すると、ディスクに保存する必要はありませんが、特定の制限を超えるとメモリ不足の例外がスローされます。

private async Task<Stream> ReadStream()
{
    Stream stream = null;
    var provider = new MultipartMemoryStreamProvider();
    await Request.Content.ReadAsMultipartAsync(provider);
    foreach (var file in provider.Contents)
    {
        var buffer = await file.ReadAsByteArrayAsync();
        stream = new MemoryStream(buffer);
    }

    return stream;
}

private async Task<Stream> ReadLargeStream()
{
    Stream stream = null;
    string root = Path.GetTempPath();
    var provider = new MultipartFormDataStreamProvider(root);
    await Request.Content.ReadAsMultipartAsync(provider);
    foreach (var file in provider.FileData)
    {
        var path = file.LocalFileName;
        byte[] content = File.ReadAllBytes(path);
        File.Delete(path);
        stream = new MemoryStream(content);
    }

    return stream;
}


1

この質問には、.Net Coreについても多くの良い答えがあります。私は両方のフレームワークを使用していましたが、提供されたコードサンプルは問題なく動作しました。だから私はそれを繰り返しません。私の場合、重要なのは、次のようにSwaggerでファイルアップロードアクションを使用する方法です。

Swaggerのファイルアップロードボタン

これが私の要約です:

ASP .Net WebAPI 2

  • ファイルをアップロードするには:MultipartFormDataStreamProvider回答はこちらをご覧ください
  • Swaggerでの使用方法

.NET Core


1

APIコントローラ:

[HttpPost]
public HttpResponseMessage Post()
{
    var httpRequest = System.Web.HttpContext.Current.Request;

    if (System.Web.HttpContext.Current.Request.Files.Count < 1)
    {
        //TODO
    }
    else
    {

    try
    { 
        foreach (string file in httpRequest.Files)
        { 
            var postedFile = httpRequest.Files[file];
            BinaryReader binReader = new BinaryReader(postedFile.InputStream);
            byte[] byteArray = binReader.ReadBytes(postedFile.ContentLength);

        }

    }
    catch (System.Exception e)
    {
        //TODO
    }

    return Request.CreateResponse(HttpStatusCode.Created);
}

0

Matt Frearの答えを補完する-これは、ディスクからファイルを保存および読み取ることなく、Streamから直接ファイルを読み取るためのASP NET Coreの代替案です。

public ActionResult OnPostUpload(List<IFormFile> files)
    {
        try
        {
            var file = files.FirstOrDefault();
            var inputstream = file.OpenReadStream();

            XSSFWorkbook workbook = new XSSFWorkbook(stream);

            var FIRST_ROW_NUMBER = {{firstRowWithValue}};

            ISheet sheet = workbook.GetSheetAt(0);
            // Example: var firstCellRow = (int)sheet.GetRow(0).GetCell(0).NumericCellValue;

            for (int rowIdx = 2; rowIdx <= sheet.LastRowNum; rowIdx++)
               {
                  IRow currentRow = sheet.GetRow(rowIdx);

                  if (currentRow == null || currentRow.Cells == null || currentRow.Cells.Count() < FIRST_ROW_NUMBER) break;

                  var df = new DataFormatter();                

                  for (int cellNumber = {{firstCellWithValue}}; cellNumber < {{lastCellWithValue}}; cellNumber++)
                      {
                         //business logic & saving data to DB                        
                      }               
                }
        }
        catch(Exception ex)
        {
            throw new FileFormatException($"Error on file processing - {ex.Message}");
        }
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.