Cách chấp nhận POST tệp


254

Tôi đang sử dụng asp.net mvc 4 webapi beta để xây dựng dịch vụ nghỉ ngơi. Tôi cần có khả năng chấp nhận hình ảnh / tập tin đã đăng từ ứng dụng khách. Điều này có thể sử dụng webapi không? Dưới đây là cách hành động tôi hiện đang sử dụng. Có ai biết một ví dụ làm thế nào điều này nên làm việc?

[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
Điều đó chỉ hoạt động với MVC chứ không phải khung WebAPI.
Phil

Bạn chỉ có thể lấy món đồ từRequest.Files
Tejs

7
ApiControll không chứa httpRequestBase có thuộc tính Files. Đối tượng Request của nó dựa trên lớp HttpRequestMessage.
Phil

Câu trả lời:


168

xem http://www.asp.net/web-api/overview/formats-and-model-binding/html-forms-and-multipart-mime#multipartmime , mặc dù tôi nghĩ rằng bài viết này có vẻ phức tạp hơn một chút nó thực sự là

Về cơ bản,

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
Lợi ích của việc sử dụng Tác vụ để chỉ đọc một tệp là gì? Câu hỏi chính hãng, tôi mới bắt đầu sử dụng Nhiệm vụ. Theo hiểu biết hiện tại của tôi, mã này có thực sự phù hợp khi tải lên nhiều hơn một tệp đúng không?
Chris

48
MultipartFormDataStreamProvider không còn thuộc tính BodyPartFileNames nữa (trong WebApi RTM). Xem asp.net/web-api/overview/usiness-with-http/ từ
Shrike

5
Các bạn, bất kỳ ai trong số các bạn có thể làm sáng tỏ lý do tại sao chúng ta không thể đơn giản truy cập các tệp bằng cách sử dụng HttpContext.Cản.Request.Files và thay vào đó cần phải sử dụng MultipartFormDataStreamProvider ưa thích này? Câu hỏi đầy đủ: stackoverflow.com/questions/17967544 .
niaher

7
Các tệp đang được lưu dưới dạng BodyPart_8b77040b-354b-464c-bc15-b3591f98f30f . Không phải chúng nên được lưu như pic.jpg chính xác như trên máy khách sao?
lbrahim

10
MultipartFormDataStreamProviderkhông để lộ BodyPartFileNamestài sản nữa, tôi đã sử dụng FileData.First().LocalFileNamethay thế.
Chtiwi Malek

374

Tôi ngạc nhiên khi nhiều bạn dường như muốn lưu tệp trên máy chủ. Giải pháp để giữ mọi thứ trong bộ nhớ như sau:

[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
Giữ các tệp trong bộ nhớ có thể hữu ích nếu bạn không muốn sử dụng dung lượng đĩa. Tuy nhiên, nếu bạn cho phép các tệp lớn được tải lên thì việc giữ chúng trong bộ nhớ có nghĩa là máy chủ web của bạn sẽ sử dụng rất nhiều bộ nhớ, không thể chi tiêu cho việc giữ mọi thứ xung quanh cho các yêu cầu khác. Điều này sẽ gây ra vấn đề trên các máy chủ hoạt động dưới tải cao.
Willem Me gợi ý

21
@ W.Me gợi ý Tôi hiểu lý do muốn lưu trữ dữ liệu, nhưng tôi không hiểu tại sao mọi người muốn lưu trữ dữ liệu đã tải lên trên không gian đĩa máy chủ. Bạn phải luôn luôn cách ly lưu trữ tệp khỏi máy chủ web - ngay cả đối với các dự án nhỏ hơn.
Gleno

1
Đảm bảo kích thước tệp đã đăng của bạn nhỏ hơn 64k, hành vi mặc định là bỏ qua các yêu cầu nếu không, tôi đã bị kẹt trên này trong một thời gian đăng nhập.
Gary Davies

3
Thật không may, MultipartMemoryStreamProvider không trợ giúp nếu bạn muốn đọc dữ liệu biểu mẫu. Muốn tạo một cái gì đó giống như MultipartFormDataMemoryStreamProvider nhưng rất nhiều lớp và lớp trợ giúp là nội bộ trong aspnetwebstack :(
martinoss

9
File.WriteAllBytes(filename, buffer);để ghi nó vào một tập tin
pomber

118

Xem mã dưới đây, được điều chỉnh từ bài viết này , minh họa mã ví dụ đơn giản nhất tôi có thể tìm thấy. Nó bao gồm cả tập tin và bộ nhớ (nhanh hơn) tải lên.

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.C hiện tại là null khi WebAPI được lưu trữ trong OWIN là một thùng chứa tự lưu trữ.
Zach

1
Đã sửa lỗi như vậy: var httpRequest = System.Web.HttpContext.Cản.Request;
msysmilu

7
Không sử dụng System.Web trong WebAPI trừ khi bạn thực sự phải làm.
Kugel

3
Chắc chắn, System.Web được liên kết chặt chẽ với IIS. Nếu bạn đang làm việc trong dòng phần mềm OWIN hoặc .Net Core, các API đó sẽ không khả dụng khi chạy trong linux hoặc tự lưu trữ.
Kugel

2
Câu trả lời chính xác. Chỉ cần một chi tiết: nếu bạn đang tải lên từ một trang HTML, thẻ <input type = "file" /> phải có thuộc tính "name" hoặc tệp sẽ không xuất hiện trong HttpContext.Cản.Request.Files.
GBU

17

Cách ASP.NET Core hiện có tại đây :

[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 là một giải pháp nhanh và bẩn, lấy nội dung tệp được tải lên từ phần thân HTTP và ghi nó vào một tệp. Tôi đã bao gồm một đoạn mã HTML / JS "xương trần" để tải tệp lên.

Phương pháp API web:

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

Tải lên tệp 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>

Coi chừng rằng điều này sẽ không hoạt động với các hình thức tải lên nhiều phần 'bình thường'.
Tom

3
@Tom điều đó có nghĩa là gì?
Chazt3n

Điều đó có nghĩa là nó không tương thích với các trình duyệt nơi JavaScript bị vô hiệu hóa / không tồn tại, ví dụ Netscape 1. *.
Mikael Dúi Bolinder

13

Tôi đã sử dụng câu trả lời của Mike Wasson trước khi tôi cập nhật tất cả NuGets trong dự án webapi mvc4 của mình. Khi tôi đã làm, tôi phải viết lại hành động tải lên tệp:

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

Rõ ràng BodyPartFileNames không còn có sẵn trong MultipartFormDataStreamProvider.


Trong WebApi RTM, BodyPartFileNames đã được đổi thành FileData. Xem ví dụ cập nhật tại asp.net/web-api/overview/usiness-with-http/ gợi
Mark van Straten

Tại sao không chỉ sử dụng bộ sưu tập System.Web.HttpContext.Cản.Request.Files?
Kết nối

Tôi đang nghĩ đến việc sử dụng phương pháp của bạn với 2 lần đặt trước: 1) Không phải điều này sẽ viết hai lần: i) trong ReadAsMultipartAsyncvà ii) Trong File.Move? 2) Bạn có thể làm gì async File.Move?
seebcakes

1) Tôi không gặp vấn đề với hai lần ghi, url có được gọi hai lần không? 2) bạn có thể thực hiện tác vụ.Run (() => {File.Move (src, mệnh);});
Steve Stokes

10

Hướng tới cùng hướng này, tôi đang đăng một đoạn mã máy khách và máy chủ gửi Tệp Excel bằng WebApi, c # 4:

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

Và bộ điều khiển webapi máy chủ:

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

Và Custom MyMultipartFormDataStreamProvider, cần thiết để tùy biến Tên ảnh:

PS: Tôi mất mã này từ bài khác 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);
    }
}

Bạn có thể chỉ ra cách bạn gọi bạn static method SetFiletrong Bộ điều khiển của bạn?

Đây là một câu trả lời tốt. Mở rộng nhà cung cấp cơ sở như thế này cũng cho phép bạn kiểm soát luồng và cho phép bạn linh hoạt hơn so với việc chỉ cung cấp một pathbộ lưu trữ đám mây.
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
Tôi nghĩ người dùng cần một số lời giải thích ...!
kamesh

4

Đây là hai cách để chấp nhận một tập tin. Một sử dụng trong nhà cung cấp bộ nhớ MultipartMemoryStreamProvider và một sử dụng MultipartFormDataStreamProvider lưu vào đĩa. Lưu ý, điều này chỉ dành cho một tệp tải lên tại một thời điểm. Bạn có thể chắc chắn mở rộng điều này để lưu nhiều tệp. Cách tiếp cận thứ hai có thể hỗ trợ các tệp lớn. Tôi đã kiểm tra các tệp trên 200 MB và nó hoạt động tốt. Sử dụng trong phương pháp tiếp cận bộ nhớ không yêu cầu bạn lưu vào đĩa, nhưng sẽ loại bỏ ngoại lệ bộ nhớ nếu bạn vượt quá một giới hạn nhất định.

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

Câu hỏi này có rất nhiều câu trả lời hay ngay cả đối với .Net Core. Tôi đã sử dụng cả hai Framework, các mẫu mã được cung cấp hoạt động tốt. Vì vậy, tôi sẽ không lặp lại nó. Trong trường hợp của tôi, điều quan trọng là làm thế nào để sử dụng các hành động tải lên tệp với Swagger như thế này:

Nút tải lên tệp trong Swagger

Đây là tóm tắt của tôi:

ASP .Net WebAPI 2

  • Để tải lên tệp sử dụng: MultipartFormDataStreamProvider xem câu trả lời tại đây
  • Cách sử dụng với Swagger

Lõi .NET


1

Trình điều khiển 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

Bổ sung cho câu trả lời của Matt Frear - Đây sẽ là một thay thế ASP NET Core để đọc tệp trực tiếp từ Stream, mà không lưu và đọc nó từ đĩa:

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}");
        }
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.