C# 如何使用WCF 4.0模板(REST)上载文件

C# 如何使用WCF 4.0模板(REST)上载文件,c#,wcf,rest,stream,C#,Wcf,Rest,Stream,如何在RESTfulWebService中上载文件(大小500 MB及以上…),然后将文件保存到特定位置 如果您有链接,请分享。 我使用Fiddler来测试服务 以下是我一直在研究的代码 [WebInvoke(UriTemplate = "Add", Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)] public bool UploadFile(File

如何在RESTfulWebService中上载文件(大小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);
    }
}

有关如何将文件上载到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);
    }
确保设置适当的内容类型。我认为,当使用流作为参数之一时,我们不能传递多个参数,因此为了获得文件名和流,只需将所有内容作为单个流传递,然后使用一个解析器来分离流。有一种称为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; }
    }
公共类多部分解析器
{
公共多部分解析器(流)
{
Parse(stream,Encoding.UTF8);
ParseParameter(stream,Encoding.UTF8);
}
公共多部分解析器(流、编码)
{
解析(流,编码);
}
私有void解析(流、流、编码)
{
成功=错误;
//将流读入字节数组
字节[]数据=ToByteArray(流);
//复制到字符串以进行头解析
字符串内容=encoding.GetString(数据);
//第一行应该包含分隔符
int delimiterRendIndex=content.IndexOf(“\r\n”);
如果(分隔符索引>-1)
{
字符串分隔符=content.Substring(0,content.IndexOf(“\r\n”);
//查找内容类型

Regex re=new Regex(@“(?有关如何将文件上载到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);
    }
请确保设置适当的内容类型。我认为,当使用流作为参数之一时,我们不能传递多个参数,因此,为了获得文件名和流,只需将所有内容作为单个流传递,然后使用将分离流的解析器。有一种称为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; }
    }
公共类多部分解析器
{
公共多部分解析器(流)
{
Parse(stream,Encoding.UTF8);
ParseParameter(stream,Encoding.UTF8);
}
公共多部分解析器(流、编码)
{
解析(流,编码);
}
私有void解析(流、流、编码)
{
成功=错误;
//将流读入字节数组
字节[]数据=ToByteArray(流);
//复制到字符串以进行头解析
字符串内容=encoding.GetString(数据);
//第一行应该包含分隔符
int delimiterRendIndex=content.IndexOf(“\r\n”);
如果(分隔符索引>-1)
{
字符串分隔符=content.Substring(0,content.IndexOf(“\r\n”);
//查找内容类型

Regex re=new Regex(@“(?答案来源于我以前所做的。服务可能如下所示:

[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;
    }
}
[DataContract]
public class Document
{
    [DataMember]
    public string Id
    {
       get;set;
    }

    [DataMember]
    public string Name
    {
       get;set;
    }

    /* other fields */

}
其中,文件可能如下所示:

[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;
    }
}
[DataContract]
public class Document
{
    [DataMember]
    public string Id
    {
       get;set;
    }

    [DataMember]
    public string Name
    {
       get;set;
    }

    /* other fields */

}
若要将文件上载到服务(假定位于),可以执行以下操作:

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的一部分。

答案来源于我以前所做的。服务可能如下所示:

[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;
    }
}
[DataContract]
public class Document
{
    [DataMember]
    public string Id
    {
       get;set;
    }

    [DataMember]
    public string Name
    {
       get;set;
    }

    /* other fields */

}
其中,文件可能如下所示:

[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;
    }
}
[DataContract]
public class Document
{
    [DataMember]
    public string Id
    {
       get;set;
    }

    [DataMember]
    public string Name
    {
       get;set;
    }

    /* other fields */

}
若要将文件上载到服务(假定位于),可以执行以下操作:

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。答案已相应更新。您好,先生,我收到“流不支持写入”的消息“inputStream.CopyTo(stream);”这有什么不对?你说的“//设置头是什么意思“?感谢您的耐心。至于错误,我发现我的答案排除了设置Request.Method。您必须设置它。看看它是否有任何区别。如果需要,您可以在响应上设置任何标题,包括201以及根据REST指导原则有人可以访问REST资源的位置。我确实放置了”Request.Method=“POST”已经。实际上它成功地创建了名为“Guid”的文件。但是当我打开该文件时,它不包含任何内容。感谢您指出它。它应该是file.OpenRead。答案已经相应地更新。您好,先生,我得到了“Stream不支持写入。”关于“inputStream.CopyTo(Stream);”这有什么不对?您所说的“//设置标题”是什么意思?感谢您的耐心。至于错误,我发现我的答案排除了设置Request.Method。您必须设置它。看看它是否有任何区别。您可以根据REST指南在响应上设置任何标题,包括201和某人可以访问REST资源的位置。我确实放置了“Request.Method=“POST”已经。实际上它成功地创建了名为“Guid”的文件。但是当我打开该文件时,它不包含任何内容。