2014-05-19 7 views
9

Chcę przesłać plik i wysłać wraz z plikiem kilka dodatkowych informacji, powiedzmy string foo i int bar.Metoda WebAPI, która pobiera plik i dodatkowe argumenty.

Jak napisać metodę kontrolera ASP.NET WebAPI, która odbiera przesyłanie pliku, ciąg znaków i int?

My JavaScript:

var fileInput = document.querySelector("#filePicker"); 
var formData = new FormData(); 
formData.append("file", fileInput.files[0]); 
formData.append("foo", "hello world!"); 
formData.append("bar", 42); 

var options = { 
    url: "/api/foo/upload", 
    data: formData, 
    processData: false // Prevents JQuery from transforming the data into a query string 
}; 
$.ajax(options); 

Mój kontroler WebAPI może uzyskać dostępu do pliku tak:

public async Task<HttpResponseMessage> Upload() 
{ 
    var streamProvider = new MultipartMemoryStreamProvider(); 
    await Request.Content.ReadAsMultipartAsync(streamProvider); 
    var fileStream = await streamProvider.Contents[0].ReadAsStreamAsync(); 
} 

Ale to nie jest dla mnie jasne, w jaki sposób mogę uzyskać w moim ciąg i mojego int. Sądzę, że prawdopodobnie mogę powiedzieć streamProvider.Content [1] lub cokolwiek innego, ale to jest bardzo nieprzyjemne.

Jaki jest właściwy sposób ©, aby napisać działanie WebAPI, które akceptuje przesłanie pliku, ciąg znaków i int?

+0

jestem prawdopodobnie brakuje czegoś ... Dlaczego nie można po prostu zrobić parametry foo i pasek przesyłania metody i niech wiązania magia się dzieje? – Scrappydog

+0

Skoncentrowałem się na wysyłaniu danych w FormData (np. Jako dane w $ .post (url, formData), segregator nigdy ich nie znalazł.Okazuje się, mogę wysłać plik jako dane formularza, a następnie wstaw inne argumenty w adresie URL, a wtedy moduł modelujący działa w sposób magiczny: –

Odpowiedz

8

Możesz utworzyć własną MultipartFileStreamProvider, aby uzyskać dostęp do dodatkowych argumentów.

W pętli przechodzimy przez każdy plik w formie wieloczęściowej i ładujemy dane niestandardowe (jeśli masz tylko jeden plik, będziesz miał tylko jeden obiekt na liście CustomData).

class MyCustomData 
{ 
    public int Foo { get; set; } 
    public string Bar { get; set; } 
} 

class CustomMultipartFileStreamProvider : MultipartMemoryStreamProvider 
{ 
    public List<MyCustomData> CustomData { get; set; } 

    public CustomMultipartFileStreamProvider() 
    { 
     CustomData = new List<MyCustomData>(); 
    } 

    public override Task ExecutePostProcessingAsync() 
    { 
     foreach (var file in Contents) 
     { 
      var parameters = file.Headers.ContentDisposition.Parameters; 
      var data = new MyCustomData 
      { 
       Foo = int.Parse(GetNameHeaderValue(parameters, "Foo")), 
       Bar = GetNameHeaderValue(parameters, "Bar"), 
      }; 

      CustomData.Add(data); 
     } 

     return base.ExecutePostProcessingAsync(); 
    } 

    private static string GetNameHeaderValue(ICollection<NameValueHeaderValue> headerValues, string name) 
    { 
     var nameValueHeader = headerValues.FirstOrDefault(
      x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); 

     return nameValueHeader != null ? nameValueHeader.Value : null; 
    } 
} 

Następnie w kontrolerze:

class UploadController : ApiController 
{ 
    public async Task<HttpResponseMessage> Upload() 
    { 
     var streamProvider = new CustomMultipartFileStreamProvider(); 
     await Request.Content.ReadAsMultipartAsync(streamProvider); 

     var fileStream = await streamProvider.Contents[0].ReadAsStreamAsync(); 
     var customData = streamProvider.CustomData; 

     return Request.CreateResponse(HttpStatusCode.Created); 
    } 
} 
+0

Dzięki. Skończyłem właśnie przekazując argumenty używając "prostych" argumentów, np. $ .post ("/ api/foo? id = 23 & bar = hello", fileData). To działa ... pod warunkiem, że nie przekazujesz złożonych obiektów lub tablic. Bardziej podoba mi się twoje rozwiązanie, więc zaznaczam to jako odpowiedź. –

+2

W naszej próbie otrzymujemy nazwę parametru w części wartości pary klucz/wartość - pole klucza simple zawiera "nazwę". Czy coś się zmieniło od maja? – codeputer

2

Można to zrobić poprzez następujący sposób: JQuery Metoda:

var data = new FormData(); 

    data.append("file", filesToUpload[0].rawFile); 
    var doc = {};    
    doc.DocumentId = 0; 
    $.support.cors = true; 
    $.ajax({ 
     url: '/api/document/uploaddocument', 
     type: 'POST', 
     contentType: 'multipart/form-data', 
     data: data, 
     cache: false, 
     contentType: false, 
     processData: false, 
     success: function (response) { 
      docId = response.split('|')[0]; 
      doc.DocumentId = docId; 
      $.post('/api/document/metadata', doc) 
       .done(function (response) { 
       }); 
      alert('Document save successfully!'); 
     }, 
     error: function (e) { 
      alert(e); 
     } 
    }); 

nazywają Web API UploadDocuement '

[Route("api/document/uploaddocument"), HttpPost] 
     [UnhandledExceptionFilter] 
     [ActionName("UploadDocument")] 
     public Task<HttpResponseMessage> UploadDocument() 
     { 
      // Check if the request contains multipart/form-data. 
      if (!Request.Content.IsMimeMultipartContent()) 
      { 
       Task<HttpResponseMessage> mytask = new Task<HttpResponseMessage>(delegate() 
       { 
        return new HttpResponseMessage() 
        { 
         StatusCode = HttpStatusCode.BadRequest, 
         Content = "In valid file & request content type!".ToStringContent() 
        }; 
       }); 
       return mytask; 
      } 


      string root = HttpContext.Current.Server.MapPath("~/Documents"); 
      if (System.IO.Directory.Exists(root)) 
      { 
       System.IO.Directory.CreateDirectory(root); 
      } 
      var provider = new MultipartFormDataStreamProvider(root); 

      var task = Request.Content.ReadAsMultipartAsync(provider). 
      ContinueWith<HttpResponseMessage>(o => 
      { 
       if (o.IsFaulted || o.IsCanceled) 
        throw new HttpResponseException(HttpStatusCode.InternalServerError); 

       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("\"", ""))); 

       string sFileName = provider.FileData.First().Headers.ContentDisposition.FileName.Replace("\"", ""); 

       FileInfo FInfos = new FileInfo(Path.Combine(root, guid + "_" + provider.FileData.First().Headers.ContentDisposition.FileName.Replace("\"", ""))); 

       Document dbDoc = new Document() 
       { 
        DocumentID = 0     

       }; 

       context.DocumentRepository.Insert(dbDoc); 
       context.Save(); 

       return new HttpResponseMessage() 
       { 
        Content = new StringContent(string.Format("{0}|File uploaded.", dbDoc.DocumentID)) 
       }; 
      } 
      ); 
      return task; 

     } 

Zadzwoń metadanych Web API poprzez następujący sposób:

[Route("api/document/metadata"), HttpPost] 
     [ActionName("Metadata")] 
     public Task<HttpResponseMessage> Metadata(Document doc) 
     { 
      int DocId = Convert.ToInt32(System.Web.HttpContext.Current.Request.Form["DocumentId"].ToString()); 

      Task<HttpResponseMessage> mytask = new Task<HttpResponseMessage>(delegate() 
      { 
       return new HttpResponseMessage() 
       { 
        Content = new StringContent("metadata updated") 
       }; 
      }); 
      return mytask; 
     } 
+0

Tak więc, jeśli dobrze rozumiem, wykonujesz 2 wywołania: jeden dla pliku, drugi dla metadanych (np. "dodatkowe argumenty" część mojego pytania) Czy jest sposób to zrobić w jednym wywołaniu? –

+0

wypróbuj to w apletu API UploadDocument "System.Web.HttpContext.Current.Request.Form [" DocumentId "]. ToString()" –

+0

in powyżej, robię 2 wywołania, jeden do pliku, a drugi do metadanych, można uzyskać go za pomocą jednego połączenia, jak na przykład wypróbować ten w aplecie sieci web UploadDocument "System.Web.HttpContext.Current.Request.Form [" DocumentId "] .ToString() " –

1

myślę odpowiedź tutaj są doskonałe. Inni mogą zobaczyć prosty przykład przesyłania danych poza plikiem w formie podsumowania, w tym funkcja JavaScript, która wywołuje WebAPI do kontrolera FileUpload, oraz fragment z kontrolera FileUpload (w VB.net) odczytuje dodatkowe dane przekazane z Javascript.

Javascript:

  function uploadImage(files) { 
      var data = new FormData(); 
      if (files.length > 0) { 
       data.append("UploadedImage", files[0]); 
       data.append("Source", "1") 
       var ajaxRequest = $.ajax({ 
        type: "POST", 
        url: "/api/fileupload/uploadfile", 
        contentType: false, 
        processData: false, 
        data: data 
       }); 

File Upload Kontroler:

 <HttpPost> _ 
    Public Function UploadFile() As KeyValuePair(Of Boolean, String) 
     Try 
      If HttpContext.Current.Request.Files.AllKeys.Any() Then 
       Dim httpPostedFile = HttpContext.Current.Request.Files("UploadedImage") 
       Dim source = HttpContext.Current.Request.Form("Source").ToString() 

Więc jak widać w JavaScript, dodatkowe dane przekazywane jest kluczem "Źródło", a wartość jest „1 ". I jak Chandrika odpowiedział powyżej, kontroler odczytuje to przekazane dane przez "System.Web.HttpContext.Current.Request.Form (" Source "). ToString()".

Należy zauważyć, że formularz ("Źródło") używa() (kontra []), ponieważ kod kontrolera znajduje się w VB.net.

Mam nadzieję, że to pomoże.

2

Można wyodrębnić wiele plików i wiele atrybutów w ten sposób:

public async Task<HttpResponseMessage> Post() 
{ 
    Dictionary<string,string> attributes = new Dictionary<string, string>(); 
    Dictionary<string, byte[]> files = new Dictionary<string, byte[]>(); 

    var provider = new MultipartMemoryStreamProvider(); 
    await Request.Content.ReadAsMultipartAsync(provider); 
    foreach (var file in provider.Contents) 
    { 
     if (file.Headers.ContentDisposition.FileName != null) 
     { 
      var filename = file.Headers.ContentDisposition.FileName.Trim('\"'); 
      var buffer = await file.ReadAsByteArrayAsync(); 
      files.Add(filename, buffer); 
     } else 
     { 
      foreach(NameValueHeaderValue p in file.Headers.ContentDisposition.Parameters) 
      { 
       string name = p.Value; 
       if (name.StartsWith("\"") && name.EndsWith("\"")) name = name.Substring(1, name.Length - 2); 
       string value = await file.ReadAsStringAsync(); 
       attributes.Add(name, value); 
      } 
     } 
    } 
    //Your code here 
    return new HttpResponseMessage(HttpStatusCode.OK); 
} 
Powiązane problemy