【问题标题】:How to uploadfile using WCF 4.0 Template (REST)如何使用 WCF 4.0 模板 (REST) 上传文件
【发布时间】:2016-04-09 20:58:03
【问题描述】:

如何在 Restful webservice 中上传文件(500 MB 以上...)然后将文件保存在特定位置?

如果你有链接,请分享。
我使用 Fiddler 来测试服务。

这是我一直在努力的代码。

[WebInvoke(UriTemplate = "Add", Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
public bool UploadFile(FileUploader.File userFile, System.IO.Stream fileStream)
{
    FileUploader.Logic.StreamObject streamUploader = new FileUploader.Logic.StreamObject();
    streamUploader.UploadFile(userFile.FileName, fileStream);
    return true;
}



public class StreamObject : IStreamObject
{
    public void UploadFile(string filename, Stream fileStream)
    {
        byte[] buffer = new byte[10000];
        int bytesRead, totalbytesRead = 0;
        do
        {
            bytesRead = fileStream.Read(buffer, 0, buffer.Length);
            totalbytesRead += bytesRead;
        } while (bytesRead > 0);
    }
}

【问题讨论】:

    标签: c# wcf rest stream


    【解决方案1】:

    如何将文件上传到 REST 服务的示例如下所示:

    private byte[] UseWebClientForFileUpload(string serviceBaseUrl, String resourceUrl, string filePath)
        {
            var c = new WebClient();
            c.OpenWrite(string.Concat(serviceBaseUrl, resourceUrl), "POST");
            c.Headers[HttpRequestHeader.ContentType] = "application/octet-stream";            
            return c.UploadFile(string.Concat(serviceBaseUrl, resourceUrl), filePath);
        }
    

    确保设置适当的内容类型。我认为当使用 Stream 作为参数之一时,我们不能传递多个参数,因此为了获取文件名和流,只需将所有内容作为单个流传递,然后使用可以分离您的流的解析器。有一种叫做 multipartParser 的东西,如下所示:

    public class MultipartParser
        {
            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);
    
                // 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 = ToByteArray(stream);
    
                // 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; }
        }
    

    我确实从 here 获得了 multipartParser

    【讨论】:

      【解决方案2】:

      答案源于我之前做过的事情。该服务可能如下所示:

      [ServiceContract]
      public class DocumentService
      {
          [OperationContract]
          [WebTemplate("{name}"]
          public Document Create(Stream stream, string name)
          {
              var id = Guid.NewGuid().ToString("N");
              using(FileStream outputStream = File.Create(Path.Combine("c:\\temp\\", id)))
              {
                  stream.CopyTo(outputStream);
              }
              Document document = new Document(); 
              document.Name = name;
              document.Id = id;
              // Save document to database
              // Set headers 
              return document;
          }
      }
      

      其中 Document 可能如下所示:

      [DataContract]
      public class Document
      {
          [DataMember]
          public string Id
          {
             get;set;
          }
      
          [DataMember]
          public string Name
          {
             get;set;
          }
      
          /* other fields */
      
      }
      

      要将文件上传到服务(假设它位于http://api.example.com/documents/),您可以执行以下操作:

      string name = "mydocument.doc";
      var request = WebRequest.Create ("http://api.example.com/documents/" + name);
      request.ContentType = "application/octet-stream";
      request.Method = "POST";
      using(var stream = request.GetRequestStream())
      {
          using(var inputStream = File.OpenRead("c:\\mydocument.doc"))
          {
              inputStream.CopyTo(stream);
          }
      }
      
      using(var response = (HttpWebResponse)request.GetResponse())
      {
          // process the response, if needed
      }
      

      如果您所做的只是发送一个流,则无需发送多部分流。任何参数(例如名称)都应该是 UriTemplate 的一部分,这一点很重要。

      【讨论】:

      • 感谢您的指出。它应该是 File.OpenRead。答案已相应更新。
      • 您好,先生,我收到“Stream 不支持写入”。关于“inputStream.CopyTo(流);”这有什么问题? “//设置标题”是什么意思?感谢您的耐心。
      • 至于错误,我发现我的答案排除了设置Request.Method。你必须设置这个。看看有没有什么不同。
      • 如果需要,您可以在响应中设置任何标头,包括 201 以及某人可以根据 REST 指南访问 REST 资源的位置。
      • 我确实把 "request.Method = "POST";"已经。实际上它成功地创建了带有“Guid”名称的文件。但是当我打开文件时,它什么都没有。
      猜你喜欢
      • 1970-01-01
      • 2011-06-11
      • 1970-01-01
      • 1970-01-01
      • 2011-09-09
      • 1970-01-01
      • 1970-01-01
      • 2011-09-13
      • 1970-01-01
      相关资源
      最近更新 更多