16

I send a multipart form data to my Web API like this:

string example = "my string";
HttpContent stringContent = new StringContent(example);
HttpContent fileStreamContent = new StreamContent(stream);
using (var client = new HttpClient())
{
    using (var content = new MultipartFormDataContent())
    {
         content.Add(stringContent, "example", "example");
         content.Add(fileStreamContent, "stream", "stream");
         var uri = "http://localhost:58690/api/method";
         HttpResponseMessage response = await client.PostAsync(uri, content);

and this is the Web API:

[HttpPost]
[Route("api/method")]
public async Task<HttpResponseMessage> Method()
    {
         // take contents and do something
    }

How read the string and the stream from request body in my Web API?

Giobbo
  • 249
  • 1
  • 2
  • 12

7 Answers7

17

This should help you get started:

 var uploadPath = HostingEnvironment.MapPath("/") + @"/Uploads";
 Directory.CreateDirectory(uploadPath);
 var provider = new MultipartFormDataStreamProvider(uploadPath);
 await Request.Content.ReadAsMultipartAsync(provider);

 // Files
 //
 foreach (MultipartFileData file in provider.FileData)
 {
     Debug.WriteLine(file.Headers.ContentDisposition.FileName);
     Debug.WriteLine("File path: " + file.LocalFileName);
 }

 // Form data
 //
 foreach (var key in provider.FormData.AllKeys)
 {
     foreach (var val in provider.FormData.GetValues(key))
     {
          Debug.WriteLine(string.Format("{0}: {1}", key, val));
     }
 }
Big Daddy
  • 5,160
  • 5
  • 46
  • 76
  • Thank you! If I don't need to save the parameters on the server where is my Web API, but only manipulate their, the procedure is the same? – Giobbo Nov 16 '16 at 12:32
  • @Giobbo...I'm not sure what you mean by save the parameters. Do you mean the request body's contents (files, etc.)? If so, no, you can keep them in memory, but be careful with large files. – Big Daddy Nov 16 '16 at 12:35
  • Yes, sorry (my english is terrible). I mean the body contents, stream and string in my example. I want simply take from the body and do something with their. – Giobbo Nov 16 '16 at 12:56
13

This is code i've used before to receive json data + an optional file:

var result = await Request.Content.ReadAsMultipartAsync();

var requestJson = await result.Contents[0].ReadAsStringAsync();
var request = JsonConvert.DeserializeObject<MyRequestType>(requestJson);

if (result.Contents.Count > 1)
{
    var fileByteArray = await result.Contents[1].ReadAsByteArrayAsync();
    ...
}

Its really neat that you can combine different types of data in a request like this.

Edit: an example of how to send this request:

let serialisedJson = JSON.stringify(anyObject);
let formData = new FormData();
formData.append('initializationData', serialisedJson);
// fileObject is an instance of File
if (fileObject) {
    // the 'jsonFile' name might cause some confusion: 
    // in this case, the uploaded file is actually a textfile containing json data
    formData.append('jsonFile', fileObject);
}

return new Promise((resolve, reject) => {
    let xhr = new XMLHttpRequest();
    xhr.open('POST', 'http://somewhere.com', true);
    xhr.onload = function(e: any) {
        if (e.target.status === 200) {
            resolve(JSON.parse(e.target.response));
        }
        else {
            reject(JSON.parse(e.target.response));
        }
    };
    xhr.send(formData);
});
Davy
  • 6,295
  • 5
  • 27
  • 38
  • What does the request look like? – hashtable Jun 29 '17 at 19:24
  • 1
    For this example, in my clientside code i instantiate a FormData class, to which i then add the serialized json (so not the object itself, but the stringified version), and optionally i might also add a File object, then i send the FormData instance with the XmlHttpRequest (im actually using angular2 to do this request, but the Http service of angular does not allow you to do this, so you need to handle the XmlHttpRequest yourself) – Davy Jun 30 '17 at 07:28
  • I actually just got sending a FormData object with some fields and a file via angular $http service working for my project last night. The main oddness was that I had to explicitly set the content-type to undefined.. and I didn't serialize anything, just set the body to the FormData object. Im using Angular 1.6 though. – hashtable Jun 30 '17 at 18:39
  • Oh and you're using an xml request, mine ended up being a form/multi-part, interesting.. :) – hashtable Jun 30 '17 at 18:41
8

You can read content and get all file information (in my example image) without copying to local disk in this way:

public async Task<IHttpActionResult> UploadFile()
{
    if (!Request.Content.IsMimeMultipartContent())
    {
        return StatusCode(HttpStatusCode.UnsupportedMediaType);
    }        

    var filesReadToProvider = await Request.Content.ReadAsMultipartAsync();

    foreach (var stream in filesReadToProvider.Contents)
    {
        // Getting of content as byte[], picture name and picture type
        var fileBytes = await stream.ReadAsByteArrayAsync();
        var pictureName = stream.Headers.ContentDisposition.FileName;
        var contentType = stream.Headers.ContentType.MediaType;
    }
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
1

For sending more than one file

        System.Web.HttpFileCollection hfc = System.Web.HttpContext.Current.Request.Files;

        //// CHECK THE FILE COUNT.
        for (int iCnt = 0; iCnt <= hfc.Count - 1; iCnt++)
        {
            System.Web.HttpPostedFile hpf = hfc[iCnt];
            string Image = UploadDocuments.GetDocumentorfileUri(hpf);
            UploadDocuments.UploadDocumentsIntoData(Image, hpf.FileName, id);

        }

Sending HTML Form Data in ASP.NET Web API: File Upload and Multipart MIME

MMM
  • 3,132
  • 3
  • 20
  • 32
0
 // read the file content without copying to local disk and write the content byte to file 
       try
       {
           var filesReadToProvider = await Request.Content.ReadAsMultipartAsync();
           JavaScriptSerializer json_serializer = new JavaScriptSerializer();

           foreach (var stream in filesReadToProvider.Contents)
           {

               //getting of content as byte[], picture name and picture type
               var fileBytes = await stream.ReadAsByteArrayAsync();
               var fileName = stream.Headers.ContentDisposition.Name;

               var pictureName = stream.Headers.ContentDisposition.FileName;
               var contentType = stream.Headers.ContentType.MediaType;
               var path = Path.Combine(HttpContext.Current.Server.MapPath("~/Images/Upload/"), json_serializer.Deserialize<string>(pictureName));

               File.WriteAllBytes(path, fileBytes);
           }

           return Request.CreateResponse(HttpStatusCode.OK);
       }catch(Exception ex)
       {
           return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
       }
Ishwor Khanal
  • 1,312
  • 18
  • 30
0

//Web User Interface Method protected void btnPrescAdd_Click(object sender, EventArgs e) {

        NameValueCollection collection = new NameValueCollection();

        collection.Set("c1", Session["CredID"].ToString());
        collection.Set("p1", "");
        collection.Set("p2", Request.Form["ctl00$MainContent$hdnHlthId"]);
        collection.Set("p3", Request.Form["ctl00$MainContent$PresStartDate"]);
        collection.Set("p4", Request.Form["ctl00$MainContent$PrescEndDate"]);

        FileUpload fileUpload = PrescUpload;

        ApiServices<Status> obj = new ApiServices<Status>();
        Status objReturn = obj.FetchObjectUploadAPI("POSTUHRPL", collection, 
         fileUpload, ApiServices<Status>.ControllerType.DU);

    }

//Request Method

  public T1 FetchObjectUploadAPI(string strAPIMethod, NameValueCollection collection, FileUpload file, ControllerType enObj)
    {
        T1 objReturn;
        try
        {
            string url = strWebAPIUrl + getControllerName(enObj) + strAPIMethod;

            MultipartFormDataContent content = new MultipartFormDataContent();



            int count = collection.Count;
            List<string> Keys = new List<string>();
            List<string> Values = new List<string>();

            //MemoryStream filedata = new MemoryStream(file);
            //Stream  stream = filedata;
            for (int i = 0; i < count; i++)
            {
                Keys.Add(collection.AllKeys[i]);
                Values.Add(collection.Get(i));

            }

            for (int i = 0; i < count; i++)
            {
                content.Add(new StringContent(Values[i], Encoding.UTF8, "multipart/form-data"), Keys[i]);
            }

            int fileCount = file.PostedFiles.Count();

            HttpContent filecontent = new StreamContent(file.PostedFile.InputStream);


            content.Add(filecontent, "files");

            HttpClient client = new HttpClient();


            HttpResponseMessage response = client.PostAsync(url, content).Result;

            if (response.IsSuccessStatusCode)
            {
                objReturn = (new JavaScriptSerializer()).Deserialize<T1>(response.Content.ReadAsStringAsync().Result);
            }
            else
                objReturn = default(T1);

        }
        catch (Exception ex)
        {
            Logger.WriteLog("FetchObjectAPI", ex, log4net_vayam.Constants.levels.ERROR);
            throw (ex);
        }
        return objReturn;

    }

https://stackoverflow.com/users/9600164/gaurav-sharma

0

Super late to the party, but anyone else having to work with ASP.NET Web API (1/2):

    [HttpPost, Route("img/up")]
    public async Task<IHttpActionResult> ItemImage()
    {
        var data = HttpContext.Current.Request.Form["example"];
        var item = JsonConvert.DeserializeObject<Example>(data);
        if (item == null) return BadRequest("Invalid request: Example cannot be null");

        var path = HostingEnvironment.MapPath("/") + @"img";
        if (!Directory.Exists(path))
            Directory.CreateDirectory(path);

        try
        {
            var image = HttpContext.Current.Request.Files["stream"];
            if (image == null) return BadRequest("Invalid request: no image received.");
            path = $@"{path}\{DateTime.Now:MMddyyHHmmss}.png";
            image.SaveAs(path);
        }
        catch (Exception e)
        {
            // TODO: Document Exception
        }
        return Ok();
    }

Here you can see that we're accepting HttpPost requests to /api/img/up endpoint. Currently we're not checking Mime type but you can if desired.

First we get the Form Data for your "example" and deserialize it from json to the Example class (replace this with what you're using as your model)

Then we make sure the img directory exists (modify the path if you want it somewhere outside of your API files)

Then we get the stream object from the HttpContext.Current.Request.Files["stream"] I took this snippet from an endpoint where the filename is a timestamp (to support multiple images for the same identifier)

I tried to keep it simple and sweet, it can be a tad abstract because of the Client / Server layering.

If you need to test I recommend PostMan:

Sample Postman Test

nulltron
  • 637
  • 1
  • 9
  • 25