如何使用WCF 4.0模板(REST)上传文件
本文关键字:REST 文件 模板 何使用 WCF | 更新日期: 2023-09-27 18:27:32
如何在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);
}
}
下面显示了如何将文件上传到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; }
}
我确实从这里的得到了multipartParser
答案来源于我以前所做的。该服务可能如下所示:
[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 */
}
要将文件上载到服务(假设它位于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的一部分。