파일 POST를 수락하는 방법


254

asp.net mvc 4 webapi 베타를 사용하여 나머지 서비스를 작성하고 있습니다. 클라이언트 응용 프로그램에서 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 클래스를 기반으로합니다.
Phil

답변:


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
하나의 파일 만 읽는 작업을 사용하면 어떤 이점이 있습니까? 진짜 질문, 방금 작업을 사용하기 시작했습니다. 내 현재 이해 에서이 코드는 둘 이상의 파일을 올바로 업로드 할 때 실제로 적합합니까?
Chris

48
MultipartFormDataStreamProvider에는 더 이상 BodyPartFileNames 속성이 없습니다 (WebApi RTM에서). asp.net/web-api/overview/working-with-http/…
Shrike

5
여러분, HttpContext.Current.Request.Files를 사용하여 단순히 파일에 액세스 할 수없고 대신이 멋진 MultipartFormDataStreamProvider를 사용해야하는 이유에 대해 설명해 주시겠습니까? 전체 질문 : stackoverflow.com/questions/17967544 .
niaher

7
파일이 BodyPart_8b77040b-354b-464c-bc15-b3591f98f30f 로 저장되고 있습니다 . 클라이언트에있는 것처럼 pic.jpg 처럼 정확하게 저장 해서는 안 됩니까?
lbrahim

10
MultipartFormDataStreamProviderBodyPartFileNames더 이상 속성을 노출시키지 FileData.First().LocalFileName않고 대신 사용 했습니다.
Chtiwi Malek

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
디스크 공간을 사용하지 않으려면 파일을 메모리에 유지하는 것이 좋습니다. 그러나 큰 파일을 업로드 한 다음 메모리에 보관하면 웹 서버에서 많은 메모리를 사용하게되므로 다른 요청을 위해 물건을 보관하는 데 소비 할 수 없습니다. 부하가 높은 서버에서 문제가 발생할 수 있습니다.
Willem Meints

21
@ W.Meints 데이터를 저장하려는 이유를 이해하지만 서버 디스크 공간에 업로드 된 데이터를 저장하려는 이유는 이해할 수 없습니다. 소규모 프로젝트의 경우에도 항상 파일 저장소를 웹 서버와 격리시켜야합니다.
Gleno

1
게시 된 파일 크기가 64k 미만인지 확인하십시오. 기본 동작은 요청을 무시하는 것입니다. 그렇지 않으면 로그 시간 동안이 문제가 발생했습니다.
Gary Davies

3
불행히도 MultipartMemoryStreamProvider는 양식 데이터를 읽는 데 도움이되지 않습니다. MultipartFormDataMemoryStreamProvider와 같은 것을 만들려고했지만 aspnetwebstack에 너무 많은 클래스와 도우미 클래스가 있습니다. (
martinoss

9
File.WriteAllBytes(filename, buffer);파일에 쓰기
pomber

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
WebAPI가 자체 호스팅 컨테이너 인 OWIN에서 호스팅되면 HttpContext.Current는 null입니다.
Zach

1
다음과 같이 수정했습니다. var httpRequest = System.Web.HttpContext.Current.Request;
msysmilu

7
꼭 필요한 경우가 아니면 WebAPI에서 System.Web을 사용하지 마십시오.
Kugel

3
물론 System.Web은 IIS와 밀접하게 연결되어 있습니다. OWIN piple line 또는 .Net Core에서 작업하는 경우 Linux 또는 자체 호스팅에서 실행할 때 해당 API를 사용할 수 없습니다.
Kugel

2
좋은 대답입니다. 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 스 니펫을 포함 시켰습니다.

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

'정상적인'멀티 파트 형식 업로드에서는 작동하지 않습니다.
Tom

3
@ 톰 무슨 뜻입니까?
Chazt3n

이는 Netscape 1. *와 같이 JavaScript가 비활성화 / 존재하지 않는 브라우저와 호환되지 않음을 의미합니다.
Mikael Dúi Bolinder

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

분명히 MultiPartFormDataStreamProvider에서 BodyPartFileNames를 더 이상 사용할 수 없습니다.


WebApi RTM에서 BodyPartFileNames가 FileData로 변경되었습니다. asp.net/web-api/overview/working-with-http/…
Mark van Straten

왜 System.Web.HttpContext.Current.Request.Files 컬렉션을 사용하지 않습니까?
ADOConnection

나는 두 가지 예약과 함께 당신의 방법을 사용하려고 생각합니다 : 1) 두 번 쓰지 않습니까 : i) ReadAsMultipartAsync와 ii)에서 File.Move? 2) 당신은 할 수 async File.Move있습니까?
seebiscuit

1) 두 번 쓰는 데 문제가 없었습니다 .URL이 두 번 호출됩니까? 2) 할 수있는 Task.Run (() => {File.Move (src, dest);});
Steve Stokes

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

그리고 파일 이름을 사용자 정의하는 데 필요한 Custom 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

4

파일을 수락하는 두 가지 방법이 있습니다. 하나는 메모리 공급자 인 MultipartMemoryStreamProvider 를 사용하고 다른 하나 는 디스크에 저장 하는 MultipartFormDataStreamProvider 를 사용 합니다. 이는 한 번에 하나의 파일 업로드에만 해당됩니다. 여러 파일을 저장하기 위해이를 확실히 확장 할 수 있습니다. 두 번째 방법은 큰 파일을 지원할 수 있습니다. 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 코어


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.