zoukankan      html  css  js  c++  java
  • IOS And WCF 上传文件

    IOS And WCF Story

    研究IOS上传到WCF图片的小功能,WCF实现服务端的文件上传的例子很多,单独实现IOS发送图片的例子也很多,但是两个结合起来的就很少了。
    可以通过base64来上传图片,这个方式比较简单,但是我想要的是通过网络流来传送,这样以后IOS发送任何的文件,服务器不需要修改就能直接来用。想法很简单,但是历程很辛苦。。。

    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);
        }
    }];
    

    imagedata是image转换为NSdata后的值。

    这里的代码看起来很多,其实很简单,关键只有一处就是

    [formdata appendPartWithFileData:imagedata name:name fileName:filename mimeType:@"image/jpeg"];
    

    AFMultipartFormData 做了什么呢?

    查看它的appendPartWithFileData:name:filename:mimeType:源代码,我们可以看到

    - (void)appendPartWithFileData:(NSData *)data
                          name:(NSString *)name
                      fileName:(NSString *)fileName
                      mimeType:(NSString *)mimeType
    {
      NSParameterAssert(name);
      NSParameterAssert(fileName);
      NSParameterAssert(mimeType);
    
      NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
      [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name="%@"; filename="%@"", name, fileName] forKey:@"Content-Disposition"];
      [mutableHeaders setValue:mimeType forKey:@"Content-Type"];
    
      [self appendPartWithHeaders:mutableHeaders body:data];
    }
    

    multableHeaders包含了name,filename,mimeType再次查看最后一个方法的定义

     - (void)appendPartWithHeaders:(NSDictionary *)headers
                         body:(NSData *)body
    {
        NSParameterAssert(body);
    
        AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
        bodyPart.stringEncoding = self.stringEncoding;
        bodyPart.headers = headers;
        bodyPart.boundary = self.boundary;
        bodyPart.bodyContentLength = [body length];
        bodyPart.body = body;
    
        [self.bodyStream appendHTTPBodyPart:bodyPart];
    }
    

    [self.bodyStream appendHTTPBodyPart:bodyPart];看到这里的时候我们已经明白了,以上所有包含的数据全部放到网络流里了。为什么要查看这些呢?因为WCF需要契约定义,我们不知道AFNetwork发送的时候我们应该用什么契约来接受这个方法。

    WCF接受

    通过上面的分析,我们已经大概知道契约的定义了

    void(Stream requestStream)
    

    WCF在接受数据之前还需要进行配置,在

    <system.serviceModel>
    <bindings>
      <webHttpBinding>
        <binding name="WebConfiguration"
                 maxBufferSize="65536"
                 maxReceivedMessageSize="2000000000"
                 transferMode="Streamed">
        </binding>
      </webHttpBinding>
      </bindings>
    
    <services>      
      <!--文件服务-->
      <service name="WcfServiceForIOS.ServiceForIOSFile" behaviorConfiguration="ServiceBehavior">
        <endpoint address=""  binding="webHttpBinding" behaviorConfiguration="web"   bindingConfiguration="WebConfiguration"  contract="WcfServiceForIOS.IServiceForIOSFile" />
      </service>
      
    </services>
    <behaviors>
      <endpointBehaviors>
        <behavior name="web">
          <webHttp />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="ServiceBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>      
      </serviceBehaviors>
    </behaviors>
    </system.serviceModel>
    

    尝试实现契约的主要代码

    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();                  
               
                 
    }
    

    等IOS上传图片后,可以在相应的文件夹中找到生成的图片,但是不幸的是我们无法打开,提示图片损坏太大。用NotePad++打开这个文件流,可以看到以下代码

    --Boundary+4AA85CFEE4A1D140
    Content-Disposition: form-data; name="file"; filename="test.jpg"
    Content-Type: image/jpeg
    (乱码,目测是图片的数据流)
    --Boundary+4AA85CFEE4A1D140--
    

    是不是和发送时候的很眼熟,这样和前面的分析就对上了,传输过来的文件流是包含图片的信息和图片的数据。需要分开处理。
    处理方法和web发送的form-data是一样的,先编码为string,通过正则表达取出各个属性值

     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("
    ");
    
            if (delimiterEndIndex > -1)
            {
                string delimiter = content.Substring(0, content.IndexOf("
    "));
    
                // Look for Content-Type
                Regex re = new Regex(@"(?<=Content-Type:)(.*?)(?=
    
    )");
                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 + "
    
    ".Length;
    
                    byte[] delimiterBytes = encoding.GetBytes("
    " + 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;
                }
            }
        } 
    

    取出各个段后就能用来存文件了

    参考连接

  • 相关阅读:
    Spring事务的一些基本知识(一)
    Redis管道
    Spring事务的一些基本知识(四)大事务的危害与优化
    登录页面测试点
    朋友圈点赞用例的设计点
    面向对象
    函数的重载
    构造代码块和静态代码块,构造函数的执行
    单例设计模式
    三分查找(2020icp南京F)
  • 原文地址:https://www.cnblogs.com/keithmoring/p/4206961.html
Copyright © 2011-2022 走看看