2012-03-16 9 views
5

Mam obecnie problem podczas przesyłania pliku z HTML do mojej usługi rest (WCF REST). Przesyłając plik, chcę wysłać informacje takie jak tytuł i opis wraz z zawartością pliku.Prześlij plik z formularza HTML (wieloczęściowe/formularzowe) do usługi WCF REST jako strumień bez przesyłania strumieniowego danych wejściowych całego formularza?

Więc stworzyliśmy formę testową takiego:

<form id="testForm" action="http://localhost.:1576/NotepadService.svc/Note/91f6413c-4d72-42ca-a0f3-38df15759fc9/Attachment" method="POST" enctype="multipart/form-data"> 
     <table> 
      <tr><td>Title:</td><td><input type="text" name="Title"></td></tr> 
      <tr><td>Description:</td><td><input type="text" name="Description"></td></tr> 
      <tr><td>Filename:</td><td><input type="text" name="Filename"></td></tr> 
      <tr><td>File:</td><td><input type="file" name="Contents"></td></tr> 
      <tr><td/><td><input type="submit" value="Send"></td></tr> 
     </table> 
    </form> 

po stronie serwera, chciałbym przełożyć go do tej metody:

[OperationContract] 
     [WebInvoke(
      BodyStyle = WebMessageBodyStyle.Bare, 
      Method = "POST", 
      UriTemplate = "/Note/{noteId}/Attachment")] 
     [Description("Add an attachment to a Note.")] 
     void AddAttachmentToNote(string noteId, AttachmentRequestDto attachmentRequestDto); 

Z AttachmentRequestDto zdefiniowany jako

[DataContract] 
    public class AttachmentRequestDto 
    { 
     [DataMember] 
     public string Title { get; set; } 
     [DataMember] 
     public string Description { get; set; } 
     [DataMember] 
     public string Filename { get; set; } 
     [DataMember] 
     public Stream Contents { get; set; } 
    } 

Krótko mówiąc, chciałbym uzyskać tytuł i opis jako wartości ciągów, jednocześnie uzyskując nts pliku jako strumień. Nie wydaje się to działać, ponieważ formularz HTML umieści całą treść formularza (a więc także tytuł i opis) w strumieniu wraz z zawartością pliku. Nich, określając moją metodę REST jako

[OperationContract] 
     [WebInvoke(
      BodyStyle = WebMessageBodyStyle.Bare, 
      Method = "POST", 
      UriTemplate = "/Note/{noteId}/Attachment")] 
     [Description("Add an attachment to a Note.")] 
     void AddAttachmentToNote(string noteId, Stream formContents); 

prac, ale potem muszę analizować strumienia, aby wszystkie moje dane (co nie jest miłe podejście, w porównaniu do tego, co rzeczywiście chcesz zrobić).

Może muszę zdefiniować dwie różne metody obsługi, jedna akceptująca tylko plik, a druga akceptująca szczegóły pliku? Oznaczałoby to jednak, że moje reguły biznesowe (wymagany tytuł i wymagane filece) powinny być walidowane w inny sposób (ponieważ REST jest bezpaństwowcem).

Coś, o czym warto wspomnieć: muszę zapisać zawartość pliku w bazie danych, a nie w systemie plików.

Czy ktoś ma jakieś dane wejściowe na ten temat? Trochę utknąłem na tym ...

Dzięki!

Odpowiedz

6

Proszę znaleźć jakiś kod, który może pomóc zdać pliku wraz z jego szczegóły w pojedynczym wywołaniu usługi REST:

Najpierw będzie trzeba coś o nazwie MultipartParser jak pokazano poniżej:

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Text; 
using System.Text.RegularExpressions; 

namespace SampleService 
{ 
    public class MultipartParser 
    { 
     private byte[] requestData; 
     public MultipartParser(Stream stream) 
     { 
      this.Parse(stream, Encoding.UTF8); 
      ParseParameter(stream, Encoding.UTF8); 
     } 

     public MultipartParser(Stream stream, Encoding encoding) 
     { 
      this.Parse(stream, encoding); 
     } 

     private void Parse(Stream stream, Encoding encoding) 
     { 
      this.Success = false; 

      // Read the stream into a byte array 
      byte[] data = ToByteArray(stream); 
      requestData = data; 

      // Copy to a string for header parsing 
      string content = encoding.GetString(data); 

      // The first line should contain the delimiter 
      int delimiterEndIndex = content.IndexOf("\r\n"); 

      if (delimiterEndIndex > -1) 
      { 
       string delimiter = content.Substring(0, content.IndexOf("\r\n")); 

       // Look for Content-Type 
       Regex re = new Regex(@"(?<=Content\-Type:)(.*?)(?=\r\n\r\n)"); 
       Match contentTypeMatch = re.Match(content); 

       // Look for filename 
       re = new Regex(@"(?<=filename\=\"")(.*?)(?=\"")"); 
       Match filenameMatch = re.Match(content); 

       // Did we find the required values? 
       if (contentTypeMatch.Success && filenameMatch.Success) 
       { 
        // Set properties 
        this.ContentType = contentTypeMatch.Value.Trim(); 
        this.Filename = filenameMatch.Value.Trim(); 

        // Get the start & end indexes of the file contents 
        int startIndex = contentTypeMatch.Index + contentTypeMatch.Length + "\r\n\r\n".Length; 

        byte[] delimiterBytes = encoding.GetBytes("\r\n" + delimiter); 
        int endIndex = IndexOf(data, delimiterBytes, startIndex); 

        int contentLength = endIndex - startIndex; 

        // Extract the file contents from the byte array 
        byte[] fileData = new byte[contentLength]; 

        Buffer.BlockCopy(data, startIndex, fileData, 0, contentLength); 

        this.FileContents = fileData; 
        this.Success = true; 
       } 
      } 
     } 

     private void ParseParameter(Stream stream, Encoding encoding) 
     { 
      this.Success = false; 

      // Read the stream into a byte array 
      byte[] data; 
      if (requestData.Length == 0) 
      { 
       data = ToByteArray(stream); 
      } 
      else { data = requestData; } 
      // Copy to a string for header parsing 
      string content = encoding.GetString(data); 

      // The first line should contain the delimiter 
      int delimiterEndIndex = content.IndexOf("\r\n"); 

      if (delimiterEndIndex > -1) 
      { 
       string delimiter = content.Substring(0, content.IndexOf("\r\n")); 
       string[] splitContents = content.Split(new[] {delimiter}, StringSplitOptions.RemoveEmptyEntries); 
       foreach (string t in splitContents) 
       { 
        // Look for Content-Type 
        Regex contentTypeRegex = new Regex(@"(?<=Content\-Type:)(.*?)(?=\r\n\r\n)"); 
        Match contentTypeMatch = contentTypeRegex.Match(t); 

        // Look for name of parameter 
        Regex re = new Regex(@"(?<=name\=\"")(.*)"); 
        Match name = re.Match(t); 

        // Look for filename 
        re = new Regex(@"(?<=filename\=\"")(.*?)(?=\"")"); 
        Match filenameMatch = re.Match(t); 

        // Did we find the required values? 
        if (name.Success || filenameMatch.Success) 
        { 
         // Set properties 
         //this.ContentType = name.Value.Trim(); 
         int startIndex; 
         if (filenameMatch.Success) 
         { 
          this.Filename = filenameMatch.Value.Trim(); 
         } 
         if(contentTypeMatch.Success) 
         { 
          // Get the start & end indexes of the file contents 
          startIndex = contentTypeMatch.Index + contentTypeMatch.Length + "\r\n\r\n".Length; 
         } 
         else 
         { 
          startIndex = name.Index + name.Length + "\r\n\r\n".Length; 
         } 

         //byte[] delimiterBytes = encoding.GetBytes("\r\n" + delimiter); 
         //int endIndex = IndexOf(data, delimiterBytes, startIndex); 

         //int contentLength = t.Length - startIndex; 
         string propertyData = t.Substring(startIndex - 1, t.Length - startIndex); 
         // Extract the file contents from the byte array 
         //byte[] paramData = new byte[contentLength]; 

         //Buffer.BlockCopy(data, startIndex, paramData, 0, contentLength); 

         MyContent myContent = new MyContent(); 
         myContent.Data = encoding.GetBytes(propertyData); 
         myContent.StringData = propertyData; 
         myContent.PropertyName = name.Value.Trim(); 

         if (MyContents == null) 
          MyContents = new List<MyContent>(); 

         MyContents.Add(myContent); 
         this.Success = true; 
        } 
       } 
      } 
     } 

     private int IndexOf(byte[] searchWithin, byte[] serachFor, int startIndex) 
     { 
      int index = 0; 
      int startPos = Array.IndexOf(searchWithin, serachFor[0], startIndex); 

      if (startPos != -1) 
      { 
       while ((startPos + index) < searchWithin.Length) 
       { 
        if (searchWithin[startPos + index] == serachFor[index]) 
        { 
         index++; 
         if (index == serachFor.Length) 
         { 
          return startPos; 
         } 
        } 
        else 
        { 
         startPos = Array.IndexOf<byte>(searchWithin, serachFor[0], startPos + index); 
         if (startPos == -1) 
         { 
          return -1; 
         } 
         index = 0; 
        } 
       } 
      } 

      return -1; 
     } 

     private byte[] ToByteArray(Stream stream) 
     { 
      byte[] buffer = new byte[32768]; 
      using (MemoryStream ms = new MemoryStream()) 
      { 
       while (true) 
       { 
        int read = stream.Read(buffer, 0, buffer.Length); 
        if (read <= 0) 
         return ms.ToArray(); 
        ms.Write(buffer, 0, read); 
       } 
      } 
     } 

     public List<MyContent> MyContents { get; set; } 

     public bool Success 
     { 
      get; 
      private set; 
     } 

     public string ContentType 
     { 
      get; 
      private set; 
     } 

     public string Filename 
     { 
      get; 
      private set; 
     } 

     public byte[] FileContents 
     { 
      get; 
      private set; 
     } 
    } 

    public class MyContent 
    { 
     public byte[] Data { get; set; } 
     public string PropertyName { get; set; } 
     public string StringData { get; set; } 
    } 
} 

teraz określić metodę REST, jak pokazano:

[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest)] 
AttachmentRequestDto AddAttachmentToNote(Stream stream); 

teraz implementacja powyższej metody, jak pokazano:

public AttachmentRequestDto AddAttachmentToNote(Stream stream) 
     { 
      MultipartParser parser = new MultipartParser(stream); 
      if(parser != null && parser.Success) 
      { 
       foreach (var content in parser.MyContents) 
       { 
        // Observe your string here which is a serialized version of your file or the object being passed. Based on the string do the necessary action. 
        string str = Encoding.UTF8.GetString(content.Data); 

       } 
      } 

      return new AttachmentRequestDto(); 
     } 

Moja AttachmentRequestDto wygląda w następujący sposób:

[DataContract] 
    public class AttachmentRequestDto 
    { 
     [DataMember] 
     public string Title { get; set; } 
     [DataMember] 
     public string Description { get; set; } 
     [DataMember] 
     public string Filename { get; set; } 
    } 

Teraz od klienta i wolno wykonywać POST, jak pokazano:

Image image = Image.FromFile("C:\\Users\\Guest\\Desktop\\sample.png"); 
MemoryStream ms = new MemoryStream(); 
image.Save(ms, System.Drawing.Imaging.ImageFormat.Png); 
byte[] imageArray = ms.ToArray(); 
ms.Close(); 

AttachmentRequestDto objAttachmentRequestDto = new AttachmentRequestDto(); 
objAttachmentRequestDto.Title = "Sample"; 
objAttachmentRequestDto.Description = "Sample book"; 
objAttachmentRequestDto.FileName = "SampleBook.png"; 

var serializer = new DataContractSerializer(typeof(AttachmentRequestDto)); 
var ms = new MemoryStream(); 
serializer.WriteObject(ms, objAttachmentRequestDto); 
ms.Position = 0; 
var reader = new StreamReader(ms); 
string requestBody = reader.ReadToEnd(); 

var client = new RestClient();    
client.BaseUrl = "http://localhost/SampleService/Service1.svc"; 
var request = new RestRequest(method) { DateFormat = DataFormat.Xml.ToString(), Resource = resourceUrl }; 
if(requestBody !=null) 
     request.AddParameter("objAttachmentRequestDto", requestBody); 
request.AddFile("stream", image, "Array.png"); 
var response = client.Execute(request); 

zrobić używać dll strony trzeciej do powyższego kodu zwanego RESTSharp.

Raz na serwerze The MultipartParser identyfikuje granicę żądania podziału niezbędnych treści, a następnie można zdecydować, co zrobić z każdym z treścią Split (zapisać do pliku, db, etc ..)

Wystarczy upewnij się, że posiadasz odpowiednie wpisy konfiguracyjne dla powyższego również wraz z właściwością dataContractSerializer i ustawieniem readerQuotas dla webHttpBinding.

UWAGA: MultipartParser może być w razie potrzeby bardziej rozbudowany.

+0

Wielkie dzięki. To była rzeczywiście metoda "idź do", jak próbowałem opisać w moim oryginalnym poście ("... ale potem muszę przeanalizować strumień, aby uzyskać wszystkie moje dane (co nie jest miłym podejściem, w porównaniu do tego, czego naprawdę chcę do zrobienia) ". W efekcie stworzyłem HTTPHandler o nazwie AttachmentHandler, do którego wysyłam moją treść formularza, w ten sposób otrzymuję metadane i zawartość plików oddzielnie, bez konieczności uciekania się do analizy strumieniowej. EDYCJA (ponieważ RETURN faktycznie robi post dla mnie): Jednak zaznaczam to jako odpowiedź, ponieważ sensowne jest po prostu przetwarzanie strumieniowe – StriVa

+0

Nie używałem restsharp przed. Czy możesz wyjaśnić, dlaczego używasz go powyżej? – Hoppe

+0

@Hoppe: RestSharp jest po prostu biblioteką klienta REST innej firmy, która pomaga w przeprowadzaniu żądań internetowych. Łatwa w użyciu i mniej pracy deweloperów. – Rajesh

Powiązane problemy