在做一个IOS把图片上传到服务器的小功能
IOS用的是AFNETWORKing写的图片流上传,代码如下
NSString *filename=@"test.jpg"; AFHTTPRequestOperationManager *AFManager=[[AFHTTPRequestOperationManager alloc]initWithBaseURL:[NSURL URLWithString:@FileTranUrl]]; AFHTTPRequestOperation *operation=[AFManager POST:path parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formdata){[formdata appendPartWithFileData:imagedata name:name fileName:filename mimeType:@"image/jpeg"];} success:^(AFHTTPRequestOperation *operation, id responseObject) { if (success) { success(operation,responseObject); } } failure:^(AFHTTPRequestOperation *operation, NSError *error) { if (failure) { failure(operation,error); } }];
从代码中可以看出AFNetwork将图片数据,图片名称和该数据名称都传入了网络流中,我直接用C#直接将获得流保存的话生成的图片无法打开
C#主要代码
using (targetStream = new FileStream(filePathAndName, FileMode.Create, FileAccess.Write, FileShare.None)) { const int bufferLen = 4096; Byte[] buffer = new Byte[bufferLen]; int count = 0; while ((count = sourceStream.Read(buffer, 0, bufferLen)) > 0) { targetStream.Write(buffer, 0, count); filesize += count; } targetStream.Close(); sourceStream.Close(); }
我用Notepad++打开以文件流保存的这个文件,可以清楚的看到流的开头正是AFNetwork给发送的参数:
--Boundary+3064FAAD3A74E02F
Content-Disposition: form-data; name="file"; filename="test.jpg"
Content-Type: image/jpeg
??JFIF H H (这里是乱码了,应该是图片的数据部分了)
结尾--Boundary+3064FAAD3A74E02F--
现在的问题来了,从ios发过的流既包含该文件的格式,文件名等也保含了文件的数据,我查了很多C#关于流的操作,没有发现可以分开处理的,不知道你们是怎么解决的呢?
上传图片的最后一步了,我卡主了好久了。。。。
终于被我解决了,基本方式是正则表达式匹配里面key如content-type,filename,匹配完后从后面都是图片流的数据,直接写就可以了,这样可以保存从ios端发来的文件名称和文件格式,其实这样已经实现保存ios发来的任何文件,不止包括图片格式
这是我在stackflow的提问原题。希望可以帮助碰到相同问题的朋友。
using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Text.RegularExpressions; namespace WcfServiceForIOS.Model { 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; } } }
如果是单个文件,调用的时候使用filename和filecontent就可以了
最后提醒一下,记得stream.close();传过来的和声明的filestream都需要关闭。
public MultipartParser(Stream stream)
{
this.Parse(stream, Encoding.UTF8);
// ParseParameter(stream, Encoding.UTF8);
}
public MultipartParser(Stream stream, Encoding encoding)
{
this.Parse(stream, encoding);
}
这个流文件: public MultipartParser(Stream stream) 是怎么获取的
@背叛的冲刷: HttpFileCollection collection = HttpContext.Current.Request.Files;是这样吗
你是把整个请求保存下来了。你应该从请求里获取文件保存下来
转成base64字符串,然后C#端转回存就是了,比你文件流好多了
亲,这个有例子么,主要是ios向C#服务器传递那里,和C#服务器端接收那里,有没有简洁的代码,ios可以直接向服务器传递字节流么,还是转换为base64比较好