解析 http-multipart 响应

Posted

技术标签:

【中文标题】解析 http-multipart 响应【英文标题】:Parsing http-multipart response 【发布时间】:2013-09-05 18:00:14 【问题描述】:

我需要从 Web 服务接收 http-multipart 响应。包含 JSON 和图像对的多部分响应。

如何分两部分处理响应(JSON 的 NSDictionary 和图像的 NSData)?

提前致谢!

[更新] 我为 NSData 写了一个类别。代码如下:

NSData+MultipartResponses.h

#import <Foundation/Foundation.h>

@interface NSData (MultipartResponses)

- (NSArray *)multipartArray;
- (NSDictionary *)multipartDictionary;

@end

NSData+MultipartResponses.m

#import "NSData+MultipartResponses.h"

@implementation NSData (MultipartResponses)

static NSMutableDictionary *parseHeaders(const char *headers)

   NSMutableDictionary *dict=[NSMutableDictionary dictionary];
   int max=strlen(headers);
   int start=0;
   int cursor=0;
   while(cursor<max)
   
      while((headers[cursor]!=':')&&(headers[cursor]!='='))
      
         cursor++;
      
      NSString *key=[[NSString alloc] initWithBytes:(headers+start) length:(cursor-start) encoding:NSASCIIStringEncoding];
      cursor++;

      while(headers[cursor]==' ')
      
         cursor++;
      
      start=cursor;
      while(headers[cursor]&&(headers[cursor]!=';')&&((headers[cursor]!=13)||(headers[cursor+1]!=10)))
      
         cursor++;
      

      NSString *value;
      if((headers[start]=='"')&&(headers[cursor-1]=='"'))
      
         value=[[NSString alloc] initWithBytes:(headers+start+1) length:(cursor-start-2) encoding:NSASCIIStringEncoding];
      
      else
      
         value=[[NSString alloc] initWithBytes:(headers+start) length:(cursor-start) encoding:NSASCIIStringEncoding];
      
      [dict setObject:value forKey:key];

      if(headers[cursor]==';')
      
         cursor++;
      
      else
      
         cursor+=2;
      

      while(headers[cursor]==' ')
      
         cursor++;
      
      start=cursor;
   
   return dict;


- (NSDictionary *)multipartDictionaryWithBoundary:(NSString *)boundary

   NSMutableDictionary *dict=[NSMutableDictionary dictionary];

   const char *bytes=(const char *)[self bytes];
   const char *pattern=[boundary cStringUsingEncoding:NSUTF8StringEncoding];

   int cursor=0;
   int start=0;
   int max=[self length];
   int keyNo=0;
   while(cursor<max)
   
      if(bytes[cursor]==pattern[0])
      
         int i;
         int patternLength=strlen(pattern);
         BOOL match=YES;
         for(i=0; i<patternLength; i++)
         
            if(bytes[cursor+i]!=pattern[i])
            
               match=NO;
               break;
            
         
         if(match)
         
            if(start!=0)
            
               int startOfHeaders=start+2;
               int cursor2=startOfHeaders;
               while((bytes[cursor2]!=(char)0x0d)||(bytes[cursor2+1]!=(char)0x0a)||(bytes[cursor2+2]!=(char)0x0d)||(bytes[cursor2+3]!=(char)0x0a))
               
                  cursor2++;
                  if(cursor2+4==max)
                  
                     break;
                  
               
               if(cursor2+4==max)
               
                  break;
               
               else
               
                  int lengthOfHeaders=cursor2-startOfHeaders;
                  char *headers=(char *)malloc((lengthOfHeaders+1)*sizeof(char));
                  strncpy(headers, bytes+startOfHeaders, lengthOfHeaders);
                  headers[lengthOfHeaders]=0;

                  NSMutableDictionary *item=parseHeaders(headers);

                  int startOfData=cursor2+4;
                  int lengthOfData=cursor-startOfData-2;

                  if(([item valueForKey:@"Content-Type"]==nil)&&([item valueForKey:@"filename"]==nil))
                  
                     NSString *string=[[NSString alloc] initWithBytes:(bytes+startOfData) length:lengthOfData encoding:NSUTF8StringEncoding];
                     keyNo++;
                     [dict setObject:string forKey:[NSString stringWithFormat:@"%d", keyNo]];
                  
                  else
                  
                     NSData *data=[NSData dataWithBytes:(bytes+startOfData) length:lengthOfData];
                     [item setObject:data forKey:@"data"];
                     keyNo++;
                     [dict setObject:item forKey:[NSString stringWithFormat:@"%d", keyNo]];
                  
               
            
            cursor=cursor+patternLength-1;
            start=cursor+1;
         
      
      cursor++;
   

   return dict;


- (NSArray *)multipartArray

   NSDictionary *dict=[self multipartDictionary];
   NSArray *keys=[[dict allKeys] sortedArrayUsingSelector:@selector(localizedStandardCompare:)];
   NSMutableArray *array=[NSMutableArray array];
   for(NSString *key in keys)
   
      [array addObject:dict[key]];
   
   return array;


- (NSDictionary *)multipartDictionary

   const char *bytes=(const char *)[self bytes];
   int cursor=0;
   int max=[self length];
   while(cursor<max)
   
      if(bytes[cursor]==0x0d)
      
         break;
      
      else
      
         cursor++;
      
   
   char *pattern=(char *)malloc((cursor+1)*sizeof(char));
   strncpy(pattern, bytes, cursor);
   pattern[cursor]=0x00;
   NSString *boundary=[[NSString alloc] initWithCString:pattern encoding:NSUTF8StringEncoding];
   free(pattern);
   return [self multipartDictionaryWithBoundary:boundary];


@end

【问题讨论】:

可与本主题相关:***.com/questions/22095186/… 【参考方案1】:

对我来说,您的代码不起作用。相反,我用纯 Objective-c 重写了该代码: 注意这段代码中的(我的)边界总是有额外的——在下一个边界和最后一个边界之前——那些被剥离了。 返回一个 NSArray,每个部分都有一个 NSDictionary,包含键“headers”作为 NSDictionary,“body”作为 NSData

- (NSArray *)multipartArrayWithBoundary:(NSString *)boundary

    NSString *data = [[[NSString alloc] initWithData:self encoding:NSUTF8StringEncoding] autorelease];

    NSArray *multiparts = [data componentsSeparatedByString:[@"--" stringByAppendingString:boundary]]; // remove boundaries
    multiparts = [multiparts subarrayWithRange:NSMakeRange(1, [multiparts count]-2)]; // continued removing of boundaries

    NSMutableArray *toReturn = [NSMutableArray arrayWithCapacity:2];
    for(NSString *part in multiparts)
    
        part = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
        NSArray *separated = [part componentsSeparatedByString:@"\n\n"];

        NSMutableDictionary *headers = [NSMutableDictionary dictionaryWithCapacity:3];
        for(NSString *headerLine in [[separated objectAtIndex:0] componentsSeparatedByString:@"\n"])
        
            NSArray *keyVal = [headerLine componentsSeparatedByString:@":"];

            [headers setObject:[[keyVal objectAtIndex:1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] forKey:[[keyVal objectAtIndex:0] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
        

        [toReturn addObject:[NSDictionary dictionaryWithObjectsAndKeys:[[separated objectAtIndex:1] dataUsingEncoding:NSUTF8StringEncoding], @"body", headers, @"headers", nil]];
    

    return toReturn;

【讨论】:

以上是关于解析 http-multipart 响应的主要内容,如果未能解决你的问题,请参考以下文章

解析php中curl

2018 Multi-University Training Contest 2 部分简单题解析

交换机端口的三种工作模式(Access模式、Multi模式、Trunk模式)详细解析是啥?

高频Redis面试题解析:Redis 事务是否具备原子性?

PHP curl_multi_info_read函数

使用Getopt :: Long解析参数的最简洁方法