Parsing http-multipart response

守給你的承諾、 提交于 2019-12-11 13:06:40

问题


I need receive http-multipart responses from web service. The multipart response with pairs of JSON and image.

How to handle response in two parts (NSDictionary for JSON and NSData for image)?

Thanks in advance!

[UPDATE] I wrote a category for NSData. Code below:

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

回答1:


For me, your code didn't work. Instead, I rewrote that code in pure objective-c: Take care that (my) boundaries in this code always have additional -- in front of the next boundary and a final boundary - those are stripped. An NSArray is returned with a NSDictionary for each part, containing the keys "headers" as NSDictionary and "body" as 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;
}


来源:https://stackoverflow.com/questions/18643018/parsing-http-multipart-response

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!