2012-11-02 25 views
9

Mô tả: Tôi cần gửi chuỗi mã hóa cơ sở 64 trong thông số HTTP POST đại diện cho tệp được tải lên.Tải lên Base64 lớn trong iOS

phương pháp hiện tại: Tôi đang sử dụng một ASIFormDataRequest và mã hóa tập tin của tôi vào một chuỗi base64 như vậy:

NSData *data = [[NSFileManager defaultManager] contentsAtPath:@"my path here"]; 
NSString *base64Data = [data encodeBase64ForData]; 

Tuy nhiên khi tải lên tập tin lớn App chạy ra khỏi bộ nhớ và chết một cái chết khủng khiếp!

Đề xuất giải pháp: Hiện tại, tôi đọc tập tin từ đĩa, chuyển đổi thành chuỗi base64 thành các khối và gắn chuỗi base64 đã chuyển đổi vào tham số trong yêu cầu HTTP?

Cách hiệu quả để tránh đọc toàn bộ tệp vào bộ nhớ.

Bất kỳ đầu vào nào sẽ được đánh giá rất nhiều!

http tôi đang yêu cầu đầy đủ:

NSURL *url = [NSURL URLWithString:requestProgressURLString]; 
NSData *data = [[NSFileManager defaultManager] contentsAtPath:evidence.uri]; 
NSString *base64Data = [data encodeBase64ForData]; 
NSURL *fileURL = [[NSURL alloc] initWithString:evidence.uri]; 

request = [ASIFormDataRequest requestWithURL:url]; 

request.shouldAttemptPersistentConnection = false; 
[request setShowAccurateProgress:YES]; 

[request setPostValue:accessToken forKey:@"AccessToken"]; 
[request setPostValue:[[fileURL path] lastPathComponent] forKey:@"Filename"]; 
[request setPostValue:evidence.name forKey:@"Title"]; 
[request setPostValue:base64Data forKey:@"FileData"]; //wants string value (base64 encoded)   
[request addRequestHeader:@"Content-Type" value:@"application/x-www-form-urlencoded"]; 
[request setTimeOutSeconds:60]; 
[request setUploadProgressDelegate:self]; 
[request setDownloadProgressDelegate:self]; 
[request setDelegate:self]; 
[request setDidFinishSelector:@selector(requestDone:)]; 
[request setDidFailSelector:@selector(requestWentWrong:)]; 
+0

Tôi chưa bao giờ nhìn thấy một bộ chuyển đổi Base64 chunking, nhưng nó không phải là quá khó để thực hiện một mã nguồn mở thực hiện và biến nó thành một chunking một. Base64 không * phức tạp *. –

+1

Bạn cũng có quyền kiểm soát máy chủ web không? Có nghĩa là bạn có thể sửa đổi mã ở đó? Và khi bạn nói tập tin lớn? Lớn thế nào? 100 MB? 500 MB? 1 GB? –

+0

Hiện tại, video tải lên bị giới hạn ở 100MB, mặc dù điều này có thể tăng trong tương lai. Lý tưởng nhất là giải pháp nên hoạt động không phân biệt kích thước của quá trình tải lên, vì tôi chắc chắn mã này sẽ được sử dụng trong nhiều dự án khác. Tôi đang xem luồng tại thời điểm này ... –

Trả lời

0

Thay vì đọc các tập tin trong một hit bạn cần phải đọc nó trong khối

Bạn có thể sử dụng thêm một input stream để làm điều này của Apple có một số thông tin về here

về cơ bản bạn cần tạo một NSInputStream chỉ vào tập tin của bạn như trong ví dụ

- (void)setUpStreamForFile:(NSString *)path { 
    // iStream is NSInputStream instance variable 
    iStream = [[NSInputStream alloc] initWithFileAtPath:path]; 
    [iStream setDelegate:self]; 
    [iStream scheduleInRunLoop:[NSRunLoop currentRunLoop] 
     forMode:NSDefaultRunLoopMode]; 
    [iStream open]; 
} 

Sau đó bạn có thể đọc dữ liệu trong khối sử dụng phương pháp NSStreamDelegate - (void) dòng: (NSStream *) dòng handleEvent: (NSStreamEvent) eventCode

Again của Apple đưa ra một ví dụ về này

- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode { 

    switch(eventCode) { 
     case NSStreamEventHasBytesAvailable: 
     { 
      if(!_data) { 
       _data = [[NSMutableData data] retain]; 
      } 
      uint8_t buf[1024]; 
      unsigned int len = 0; 
      len = [(NSInputStream *)stream read:buf maxLength:1024]; 
      if(len) { 
       [_data appendBytes:(const void *)buf length:len]; 
       // bytesRead is an instance variable of type NSNumber. 
       [bytesRead setIntValue:[bytesRead intValue]+len]; 

       // DO SOMETHING with DATA HERE 

      } else { 
       NSLog(@"no buffer!"); 
      } 
      break; 
     } 

Kích thước khối bạn chọn sẽ cần cân bằng hiệu suất bộ nhớ so với hiệu suất mạng vì rõ ràng có một số chi phí với tiêu đề HTTP

Bạn cũng có thể tối ưu hóa thêm bằng cách gửi thư mục NSData của mình ectly (và do đó tiết kiệm chi phí base64) kiểm tra bài này

File Upload to HTTP server in iphone programming

3

tôi sẽ chia vấn đề này ra làm hai:

1. Mã hóa tập tin lớn để Base64 trong khối
2. Tải lên tệp đó


1. Mã hóa lớn tệp vào Base64 theo khối:

Tìm một số mã mã hóa NSData trường hợp thành Base64.Sau đó sử dụng một NSInputStream để đọc dữ liệu theo khối, mã hóa và sử dụng NSOutputStream để ghi vào tệp tạm thời. Tôi đã tìm thấy câu hỏi/câu trả lời này: Is it possible to base64-encode a file in chunks? Đặt kích thước chunk thành bội số của 3 và nó sẽ hoạt động. Bạn có thể muốn chuyển chủ đề này sang chủ đề nền.

Quan trọng: Điều này sẽ tạo một tệp tạm thời, lớn hơn bản gốc. Phải có đủ dung lượng trên thiết bị cho thời điểm đó. Sau khi bạn bắt đầu tải lên, bạn có thể xóa nó.


2. Tải lên mà nộp

Có vẻ như bạn đã này đã được thực hiện. Bạn có thể sử dụng ASIFormDataRequest chính xác như trong ví dụ của mình. Và vì nó tạo một bản sao riêng của tệp (nó tạo tệp POST nhiều phần), bạn có thể xóa bản sao của mình.

Nếu bạn muốn tối ưu hóa nó bằng cách song song các bước này, bạn sẽ cần phải tìm một cách khác để tải lên tệp nhiều phần.

Các vấn đề liên quan