使用Base64和JSON上传大图片

问题描述 投票:0回答:2

我正在使用此功能使用

JSON
将图像上传到服务器。为此,我首先将图像转换为
NSData
,然后使用
NSString
转换为
Base64
。当图像不是很大时,该方法工作正常,但当我尝试上传 2Mb 图像时,它会崩溃。

问题是,即使调用了

didReceiveResponse
方法以及返回
didReceiveData
(null)
,服务器也没有收到我的图像。起初我以为这是一个超时问题,但即使将其设置为 1000.0,它仍然不起作用。任何想法?感谢您的宝贵时间!

这是我当前的代码:

 - (void) imageRequest {

   NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.myurltouploadimage.com/services/v1/upload.json"]];

   NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
   NSString *path = [NSString stringWithFormat:@"%@/design%i.png",docDir, designNum];
   NSLog(@"%@",path);

   NSData *imageData = UIImagePNGRepresentation([UIImage imageWithContentsOfFile:path]);
   [Base64 initialize];
   NSString *imageString = [Base64 encode:imageData];

   NSArray *keys = [NSArray arrayWithObjects:@"design",nil];
   NSArray *objects = [NSArray arrayWithObjects:imageString,nil];
   NSDictionary *jsonDictionary = [NSDictionary dictionaryWithObjects:objects forKeys:keys];

   NSError *error;
   NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDictionary options:kNilOptions error:&error];

   [request setHTTPMethod:@"POST"];
   [request setValue:[NSString stringWithFormat:@"%d",[jsonData length]] forHTTPHeaderField:@"Content-Length"];
   [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
   [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
   [request setHTTPBody:jsonData];

   [[NSURLConnection alloc] initWithRequest:request delegate:self];

   NSLog(@"Image uploaded");

}

 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {

   NSLog(@"didReceiveResponse");

}

 - (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {

   NSLog(@"%@",[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);

}
ios objective-c json base64 nsurlconnection
2个回答
8
投票

我最终决定上传 Base64 图像,将其分割成更小的子字符串。为了做到这一点,并且由于我需要许多

NSURLConnections
,我创建了一个名为
TagConnection
的子类,它为每个连接提供了一个标签,这样它们之间就不会出现混淆。

然后我在

TagConnection
中创建了一个
MyViewController
属性,目的是从任何函数访问它。正如您所看到的,
-startAsyncLoad:withTag:
函数可以分配并初始化
TagConnection
,而
-connection:didReceiveData:
函数可以在我收到服务器的响应时将其删除。

参考

-uploadImage
函数,首先将图像转换为字符串,然后将其分割并将块放入JSON请求中。它会被调用,直到变量偏移量大于字符串长度,这意味着所有块都已上传。

您还可以通过每次检查服务器响应并仅在返回成功时调用

-uploadImage
函数来证明每个块已成功上传。

TagConnection.h

@interface TagConnection : NSURLConnection {
    NSString *tag;
}

@property (strong, nonatomic) NSString *tag;

- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately tag:(NSString*)tag;

@end

TagConnection.m

#import "TagConnection.h"

@implementation TagConnection

@synthesize tag;

- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately tag:(NSString*)tag {
    self = [super initWithRequest:request delegate:delegate startImmediately:startImmediately];
    
    if (self) {
        self.tag = tag;
    }
    return self;
}

- (void)dealloc {
    [tag release];
    [super dealloc];
}

@end

MyViewController.h

#import "TagConnection.h"

@interface MyViewController : UIViewController

@property (strong, nonatomic) TagConnection *conn;

MyViewController.m

#import "MyViewController.h"

@interface MyViewController ()

@end

@synthesize conn;

bool stopSending = NO;
int chunkNum = 1;
int offset = 0;

- (IBAction) uploadImageButton:(id)sender {

    [self uploadImage];

}

- (void) startAsyncLoad:(NSMutableURLRequest *)request withTag:(NSString *)tag {

    self.conn = [[[TagConnection alloc] initWithRequest:request delegate:self startImmediately:YES tag:tag] autorelease];

}

- (void) uploadImage {
    
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.mywebpage.com/upload.json"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:1000.0];
    
    NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSString *path = [NSString stringWithFormat:@"%@/design%i.png", docDir, designNum];
    NSLog(@"%@",path);
    
    NSData *imageData = UIImagePNGRepresentation([UIImage imageWithContentsOfFile:path]);
    [Base64 initialize];
    NSString *imageString = [Base64 encode:imageData];
    
    NSUInteger length = [imageString length];
    NSUInteger chunkSize = 1000;
    
    NSUInteger thisChunkSize = length - offset > chunkSize ? chunkSize : length - offset;
    NSString *chunk = [imageString substringWithRange:NSMakeRange(offset, thisChunkSize)];
    offset += thisChunkSize;
        
    NSArray *keys = [NSArray arrayWithObjects:@"design",@"design_id",@"fragment_id",nil];
    NSArray *objects = [NSArray arrayWithObjects:chunk,@"design_id",[NSString stringWithFormat:@"%i", chunkNum],nil];
    NSDictionary *jsonDictionary = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
        
    NSError *error;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDictionary options:kNilOptions error:&error];
        
    [request setHTTPMethod:@"POST"];
    [request setValue:[NSString stringWithFormat:@"%d",[jsonData length]] forHTTPHeaderField:@"Content-Length"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [request setHTTPBody:jsonData];

    [self startAsyncLoad:request withTag:[NSString stringWithFormat:@"tag%i",chunkNum]];
    
    if (offset > length) {
        stopSending = YES;
    }
    
}

- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {

    NSError *error;
    NSArray *responseData = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
    if (!responseData) {
        NSLog(@"Error parsing JSON: %@", error);
    } else {
        if (stopSending == NO) {
            chunkNum++;
            [self.conn cancel];
            self.conn = nil;
            [self uploadImage];
        } else {
            NSLog(@"---------Image sent---------");
        }
    }
    
}

@end

1
投票

请不要认为这是最后的选择,这只是我的观察。

我认为你应该以块的形式发送 NSData 而不是完整的数据。我在YouTube视频上传中看到过这样的方法。他们以许多 NSData 块的形式发送大量 NSData(视频文件的 NSData)。

他们使用相同的方法来上传大数据。

所以应该谷歌一下 YouTube 数据上传 API。您应该搜索 YouTube 上传器使用的方法。

© www.soinside.com 2019 - 2024. All rights reserved.