Dosya POST Nasıl Kabul Edilir


254

Ben bir dinlenme hizmeti oluşturmak için asp.net mvc 4 webapi beta kullanıyorum. İstemci uygulamalarından POSTed görüntüleri / dosyaları kabul edebilmem gerekiyor. Bu webapi kullanarak mümkün mü? Aşağıda şu anda kullandığım işlem var. Bunun nasıl çalışması gerektiğini bir örnek bilen var mı?

[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
Bu sadece MVC ile çalışır WebAPI çerçevesi.
Phil

Sadece öğeyi kapmak gerekirRequest.Files
Tejs

7
ApiController, Files özelliği olan HttpRequestBase içermiyor. It's Request nesnesi HttpRequestMessage sınıfını temel alır.
Phil

Yanıtlar:


168

bkz. http://www.asp.net/web-api/overview/formats-and-model-binding/html-forms-and-multipart-mime#multipartmime , ancak makalenin biraz daha karmaşık görünmesine rağmen gerçekten.

Temel olarak,

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
Tek bir dosyayı okumak için Görev kullanmanın faydası nedir? Gerçek soru, sadece Görevler'i kullanmaya başladım. Mevcut anlayışımdan, bu kod gerçekten birden fazla dosya doğru yüklerken uygun mu?
Chris

48
MultipartFormDataStreamProvider artık BodyPartFileNames özelliğine sahip değil (WebApi RTM'de). Bkz. Asp.net/web-api/overview/working-with-http/…
Shrike

5
Çocuklar, lütfen neden HttpContext.Current.Request.Files kullanarak dosyalara erişemediğimize ve bunun yerine bu süslü MultipartFormDataStreamProvider'ı kullanmamız gerektiğine ışık tutabilir misiniz? Tam soru: stackoverflow.com/questions/17967544 .
niaher

7
Dosyalar BodyPart_8b77040b-354b-464c-bc15-b3591f98f30f olarak kaydediliyor . Onlar tam olarak istemcide olduğu gibi pic.jpg gibi kaydedilmemelidir ?
lbrahim

10
MultipartFormDataStreamProviderartık BodyPartFileNamesmülkü açığa vurmuyor , FileData.First().LocalFileNamebunun yerine kullandım .
Chtiwi Malek

374

Birçoğunuzun sunucuya dosya kaydetmek istemesine şaşırıyorum. Her şeyi hafızada tutma çözümü aşağıdaki gibidir:

[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
Disk alanı harcamak istemiyorsanız dosyaları bellekte tutmak yararlı olabilir. Ancak, büyük dosyaların yüklenmesine izin verirseniz, onları bellekte tutmak, web sunucunuzun çok fazla bellek kullanacağı anlamına gelir; Bu, yüksek yük altında çalışan sunucularda sorunlara neden olur.
Willem Meints

21
@ W.Meints Veri depolamak istemenin nedenlerini anlıyorum, ancak yüklenen verileri neden sunucu disk alanında depolamak isteyeceğini anlamıyorum. Dosya depolamasını her zaman web sunucusundan izole tutmalısınız - küçük projeler için bile.
Gleno

1
Gönderilen dosya boyutunun 64k'den az olduğundan emin olun, varsayılan davranış istekleri yok saymaktır, aksi takdirde bir günlük süre için bu sorunda kalmıştım.
Gary Davies

3
Maalesef, form verilerini de okumak istiyorsanız MultipartMemoryStreamProvider yardımcı olmaz. MultipartFormDataMemoryStreamProvider gibi bir şey oluşturmak istedim ama aspnetwebstack'ta çok fazla sınıf ve yardımcı sınıf var :(
martinoss

9
File.WriteAllBytes(filename, buffer);bir dosyaya yazmak
pomber

118

Bu makaleden uyarlanmış, bulabildiğim en basit örnek kodu gösteren aşağıdaki koda bakın . Hem dosya hem de bellek (daha hızlı) yüklemelerini içerir.

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
HttpContext.Current, WebAPI kendi kendini barındıran bir kapsayıcı olan OWIN'de barındırılıyorsa null olur.
Zach

1
Bunu şu şekilde düzelttim: var httpRequest = System.Web.HttpContext.Current.Request;
msysmilu

7
Kesinlikle gerekmedikçe WebAPI'de System.Web kullanmayın.
Kugel

3
Elbette, System.Web IIS'ye sıkıca bağlıdır. OWIN piple hattı veya .Net Core'da çalışıyorsanız, bu API linux altında veya kendi kendini barındırdığı zaman kullanılamaz.
Kugel

2
Mükemmel cevap. Sadece bir ayrıntı: HTML sayfasından yüklüyorsanız, <input type = "file" /> etiketi "name" özelliğine sahip olmalı veya dosya HttpContext.Current.Request.Files dosyasında bulunmayacak.
GBU

18

ASP.NET Çekirdek yolu şimdi burada :

[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

Yüklenen dosya içeriğini HTTP gövdesinden alıp bir dosyaya yazan hızlı ve kirli bir çözüm. Dosya yüklemesi için bir "çıplak kemikler" HTML / JS snippet'i ekledim.

Web API Yöntemi:

[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 Dosya Yükleme:

<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>

Bunun 'normal' çok parçalı form yüklemeleriyle çalışmayacağını unutmayın.
Tom

3
@Tom bu ne anlama geliyor?
Chazt3n

Bu, JavaScript'in devre dışı bırakıldığı / mevcut olmadığı tarayıcılarla uyumlu olmadığı anlamına gelir, örn. Netscape 1. *.
Mikael Dúi Bolinder

13

Webapi mvc4 projemdeki tüm NuGet'leri güncellemeden önce Mike Wasson'ın cevabını kullandım. Bir kez yaptım, dosya yükleme eylemini yeniden yazmak zorunda kaldım:

    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;
    }

Görünüşe göre BodyPartFileNames artık MultipartFormDataStreamProvider içinde mevcut değil.


WebApi RTM'de BodyPartFileNames, FileData olarak değiştirildi. Asp.net/web-api/overview/working-with-http/…
Mark van Straten

Neden sadece System.Web.HttpContext.Current.Request.Files koleksiyonunu kullanmıyorsunuz?
ADOConnection

Ben yöntem 2 rezervasyonları ile kullanmayı düşünüyorum: 1) Bu iki kez yazmıyor: i) ReadAsMultipartAsyncve ii) In File.Move? 2) Yapabilir misin async File.Move?
seebiscuit

1) İki yazımla ilgili sorunum yoktu, URL iki kez mi çağrılıyor? 2) Task.Run (() => {File.Move (src, dest);});
Steve Stokes

10

Aynı yönlere doğru, Excel dosyalarını WebApi, c # 4 kullanarak gönderen bir istemci ve sunucu parçacıkları gönderiyorum:

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
    }
}

Ve sunucu webapi denetleyicisi:

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!"));
    }
}

Dosya adını özelleştirmek için gereken Özel MyMultipartFormDataStreamProvider:

PS: Bu kodu başka bir gönderiden aldım 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);
    }
}

Sizi static method SetFileDenetleyicinizde nasıl arayacağınızı gösterebilir misiniz ?

Bu iyi bir cevap. Temel sağlayıcıyı bu şekilde genişletmek aynı zamanda akışı kontrol etmenizi sağlar ve yalnızca pathbulut depolama sağlamanızdan daha fazla esneklik sağlar .
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
Bence kullanıcının biraz açıklamaya ihtiyacı var ...!
kamesh

4

İşte bir dosyayı kabul etmenin iki yolu. Biri MultipartMemoryStreamProvider bellek sağlayıcısında ve diğeri diske kaydeden MultipartFormDataStreamProvider kullanıyor . Bu işlemin aynı anda yalnızca bir dosya yüklemesi için olduğunu unutmayın. Birden fazla dosyayı kaydetmek için bunu kesinlikle genişletebilirsiniz. İkinci yaklaşım büyük dosyaları destekleyebilir. 200MB'ın üzerindeki dosyaları test ettim ve iyi çalışıyor. Bellek yaklaşımında kullanmak diske kaydetmenizi gerektirmez, ancak belirli bir sınırı aşarsanız bellek istisnasını atar.

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

Bu sorunun .Net Core için bile çok iyi cevapları var. Sağlanan kod örnekleri gayet iyi çalışır her iki Frameworks kullanıyordum. Bu yüzden tekrar etmeyeceğim. Benim durumumda önemli olan, Swagger ile Dosya yükleme eylemlerinin nasıl kullanılacağıydı :

Swagger'daki dosya yükleme düğmesi

İşte benim özeti:

ASP .Net WebAPI 2

  • Dosya yüklemek için şunu kullanın: MultipartFormDataStreamProvider yanıtları burada görebilirsiniz
  • Swagger ile nasıl kullanılır

.NET Core


1

API Denetleyicisi:

[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'ın cevabını tamamlamak - Bu, dosyayı diskten kaydetmeden ve okumaksızın doğrudan Stream'den okumak için bir ASP NET Core alternatifi olacaktır:

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}");
        }
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.