2012-04-05 29 views
20

Hãy xem xét một dịch vụ web được viết bằng ASP.NET Web API để chấp nhận bất kỳ tệp số nào dưới dạng yêu cầu 'multipart/mixed'. Các helper phương pháp mat trông như sau (giả sử _client là một thể hiện của System.Net.Http.HttpClient):Làm thế nào để thực hiện đúng một MediaTypeFormatter để xử lý các yêu cầu của loại 'multipart/mixed'?

public T Post<T>(string requestUri, T value, params Stream[] streams) 
{ 
    var requestMessage = new HttpRequestMessage(); 
    var objectContent = requestMessage.CreateContent(
     value, 
     MediaTypeHeaderValue.Parse("application/json"), 
     new MediaTypeFormatter[] {new JsonMediaTypeFormatter()}, 
     new FormatterSelector()); 

    var content = new MultipartContent(); 
    content.Add(objectContent); 
    foreach (var stream in streams) 
    { 
     var streamContent = new StreamContent(stream); 
     streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); 
     streamContent.Headers.ContentDisposition = 
      new ContentDispositionHeaderValue("form-data") 
      { 
       Name = "file", 
       FileName = "mystream.doc" 
      }; 
     content.Add(streamContent); 
    } 

    return _httpClient.PostAsync(requestUri, content) 
     .ContinueWith(t => t.Result.Content.ReadAsAsync<T>()).Unwrap().Result; 
} 

Phương pháp mà chấp nhận yêu cầu trong lớp con của ApiController có một chữ ký như sau:

public HttpResponseMessage Post(HttpRequestMessage request) 
{ 
    /* parse request using MultipartFormDataStreamProvider */ 
} 

Lý tưởng nhất, Tôi muốn định nghĩa nó như thế này, nơi liên hệ, nguồn và đích được trích xuất từ ​​nội dung 'multipart/mixed' dựa trên thuộc tính 'name' của tiêu đề 'Content-Disposition'.

public HttpResponseMessage Post(Contact contact, Stream source, Stream target) 
{ 
    // process contact, source and target 
} 

Tuy nhiên, với chữ ký hiện tại của tôi, đăng dữ liệu đến kết quả máy chủ trong một InvalidOperationException với một thông báo lỗi của:

Không 'MediaTypeFormatter' có sẵn để đọc một đối tượng kiểu ' HttpRequestMessage 'với loại phương tiện' multipart/mixed '.

Có một số ví dụ trên internet cách gửi và nhận tệp bằng ASP.NET Web API và HttpClient. Tuy nhiên, tôi đã không tìm thấy bất kỳ điều gì cho thấy làm thế nào để đối phó với vấn đề này.

Tôi bắt đầu xem xét triển khai MediaTypeFormatter tùy chỉnh và đăng ký cấu hình này với cấu hình chung. Tuy nhiên, trong khi nó rất dễ dàng để đối phó với serializing XML và JSON trong một tùy chỉnh MediaTypeFormatter, nó là không rõ ràng làm thế nào để đối phó với 'multipart/hỗn hợp' yêu cầu mà có thể khá nhiều bất cứ điều gì.

Trả lời

13

Có một cái nhìn tại diễn đàn này: http://forums.asp.net/t/1777847.aspx/1?MVC4+Beta+Web+API+and+multipart+form+data

Dưới đây là một đoạn mã (được đăng bởi imran_ku07) có thể giúp bạn thực hiện một định dạng tùy chỉnh để xử lý các multipart/form-data:

public class MultiFormDataMediaTypeFormatter : FormUrlEncodedMediaTypeFormatter 
{ 
    public MultiFormDataMediaTypeFormatter() : base() 
    { 
     this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("multipart/form-data")); 
    } 

    protected override bool CanReadType(Type type) 
    { 
     return true; 
    } 

    protected override bool CanWriteType(Type type) 
    { 
     return false; 
    } 

    protected override Task<object> OnReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext) 
    { 
     var contents = formatterContext.Request.Content.ReadAsMultipartAsync().Result; 
     return Task.Factory.StartNew<object>(() => 
     { 
      return new MultiFormKeyValueModel(contents); 
     }); 
    } 

    class MultiFormKeyValueModel : IKeyValueModel 
    { 
     IEnumerable<HttpContent> _contents; 
     public MultiFormKeyValueModel(IEnumerable<HttpContent> contents) 
     { 
      _contents = contents; 
     } 


     public IEnumerable<string> Keys 
     { 
      get 
      { 
       return _contents.Cast<string>(); 
      } 
     } 

     public bool TryGetValue(string key, out object value) 
     { 
      value = _contents.FirstDispositionNameOrDefault(key).ReadAsStringAsync().Result; 
      return true; 
     } 
    } 
} 

Sau đó, bạn cần thêm trình định dạng này vào ứng dụng của mình. Nếu thực hiện tự lưu trữ, bạn có thể chỉ cần thêm nó bằng cách bao gồm:

config.Formatters.Insert(0, new MultiFormDataMediaTypeFormatter()); 

trước khi khởi tạo lớp HttpSelfHostServer.

- EDIT -

Để phân tích các luồng nhị phân bạn sẽ cần phải định dạng khác. Đây là một trong đó tôi đang sử dụng để phân tích hình ảnh trong một trong các dự án làm việc của tôi.

class JpegFormatter : MediaTypeFormatter 
{ 
    protected override bool CanReadType(Type type) 
    { 
     return (type == typeof(Binary)); 
    } 

    protected override bool CanWriteType(Type type) 
    { 
     return false; 
    } 

    public JpegFormatter() 
    { 
     SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/jpeg")); 
     SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/jpg")); 
     SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/png")); 
    } 

    protected override Task<object> OnReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext) 
    { 
     return Task.Factory.StartNew(() => 
      { 
       byte[] fileBytes = new byte[stream.Length]; 
       stream.Read(fileBytes, 0, (int)fileBytes.Length); 

       return (object)new Binary(fileBytes); 
      }); 
    } 

    protected override Task OnWriteToStreamAsync(Type type, object value, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext, TransportContext transportContext) 
    { 
     throw new NotImplementedException(); 
    } 
} 

Trong điều khiển của bạn/hành động mà bạn sẽ muốn làm một cái gì đó dọc theo dòng:

+0

Có vẻ như để làm việc tuyệt vời với dữ liệu biểu mẫu chuẩn, nhưng tôi có một số thách thức khi tải trọng chứa luồng nhị phân. Tôi đã bỏ ý tưởng này và phân tích cú pháp tải trọng như được mô tả [ở đây] (http://www.asp.net/web-api/overview/formats-and-model-binding/html-forms-and-multipart-mime). Dịch vụ được lưu trữ trong IIS. – bloudraak

+0

Các giải pháp mà tôi đã cung cấp được áp dụng cho .Net 4.5 Beta. Bây giờ MS đang vận chuyển .Net 4.5RC có một cách tương tự nhưng sạch hơn để xử lý dữ liệu nhị phân. Thanh toán bài đăng trên blog này: http://byterot.blogspot.com/2012/04/aspnet-web-api-series-part-5.html – Jed

0

Hãy xem bài này https://stackoverflow.com/a/17073113/1944993 câu trả lời của Kiran Challa thực sự tốt đẹp!

Các phần thiết yếu:

Tuỳ Trong bộ nhớ MultiaprtFormDataStreamProvider:

public class InMemoryMultipartFormDataStreamProvider : MultipartStreamProvider 
{ 
    private NameValueCollection _formData = new NameValueCollection(); 
    private List<HttpContent> _fileContents = new List<HttpContent>(); 

    // Set of indexes of which HttpContents we designate as form data 
    private Collection<bool> _isFormData = new Collection<bool>(); 

    /// <summary> 
    /// Gets a <see cref="NameValueCollection"/> of form data passed as part of the multipart form data. 
    /// </summary> 
    public NameValueCollection FormData 
    { 
     get { return _formData; } 
    } 

    /// <summary> 
    /// Gets list of <see cref="HttpContent"/>s which contain uploaded files as in-memory representation. 
    /// </summary> 
    public List<HttpContent> Files 
    { 
     get { return _fileContents; } 
    } 

    public override Stream GetStream(HttpContent parent, HttpContentHeaders headers) 
    { 
     // For form data, Content-Disposition header is a requirement 
     ContentDispositionHeaderValue contentDisposition = headers.ContentDisposition; 
     if (contentDisposition != null) 
     { 
      // We will post process this as form data 
      _isFormData.Add(String.IsNullOrEmpty(contentDisposition.FileName)); 

      return new MemoryStream(); 
     } 

     // If no Content-Disposition header was present. 
     throw new InvalidOperationException(string.Format("Did not find required '{0}' header field in MIME multipart body part..", "Content-Disposition")); 
    } 

    /// <summary> 
    /// Read the non-file contents as form data. 
    /// </summary> 
    /// <returns></returns> 
    public override async Task ExecutePostProcessingAsync() 
    { 
     // Find instances of non-file HttpContents and read them asynchronously 
     // to get the string content and then add that as form data 
     for (int index = 0; index < Contents.Count; index++) 
     { 
      if (_isFormData[index]) 
      { 
       HttpContent formContent = Contents[index]; 
       // Extract name from Content-Disposition header. We know from earlier that the header is present. 
       ContentDispositionHeaderValue contentDisposition = formContent.Headers.ContentDisposition; 
       string formFieldName = UnquoteToken(contentDisposition.Name) ?? String.Empty; 

       // Read the contents as string data and add to form data 
       string formFieldValue = await formContent.ReadAsStringAsync(); 
       FormData.Add(formFieldName, formFieldValue); 
      } 
      else 
      { 
       _fileContents.Add(Contents[index]); 
      } 
     } 
    } 

    /// <summary> 
    /// Remove bounding quotes on a token if present 
    /// </summary> 
    /// <param name="token">Token to unquote.</param> 
    /// <returns>Unquoted token.</returns> 
    private static string UnquoteToken(string token) 
    { 
     if (String.IsNullOrWhiteSpace(token)) 
     { 
      return token; 
     } 

     if (token.StartsWith("\"", StringComparison.Ordinal) && token.EndsWith("\"", StringComparison.Ordinal) && token.Length > 1) 
     { 
      return token.Substring(1, token.Length - 2); 
     } 

     return token; 
    }} 

Bạn có thể sau đó là "MemoryMultiPartDataStreamProvider" trong bạn WebAPI như thế này:

var provider = await Request.Content.ReadAsMultipartAsync<InMemoryMultipartFormDataStreamProvider>(new InMemoryMultipartFormDataStreamProvider()); 

    //access form data 
    NameValueCollection formData = provider.FormData; 

    //access files 
    IList<HttpContent> files = provider.Files; 
Các vấn đề liên quan