Большие загрузки Base64 в iOS

Описание: Мне нужно отправить строку в кодировке base 64 в параметре HTTP POST, который представляет файл для загрузки.

Текущий метод: яиспользуя ASIFormDataRequest и кодируя мой файл в строку base64, примерно так:

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

Однако при загрузке больших файлов приложение не хватает памяти и умирает ужасной смертью!

Предложенное решение: Кто-нибудь теперь, как я мог бы, скажем, прочитать файл с диска, преобразовать его в строки base64 в чанах и присоединить преобразованную строку base64 к параметру в запросе HTTP?

Эффективно любым способом, который позволяет избежать чтения всего файла в память.

Любой вклад будет принята с благодарностью!

Мой http-код запроса полностью: I '

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:)];
 Oliver Mahoney05 нояб. 2012 г., 10:42
На данный момент загрузка ограничена 100 МБ, хотя в будущем это может увеличиться. В идеале решение должно работать независимо от размера загрузки, так как яЯ уверен, что этот код будет использоваться во многих других проектах. Я'Я смотрю на потоки в данный момент ...
 Black Frog03 нояб. 2012 г., 12:50
У вас также есть контроль над веб-сервером? То есть вы можете изменить код там же? А когда вы говорите, большой файл? На сколько огромен? 100 МБ? 500 МБ? 1 Гб?
 Evert13 нояб. 2012 г., 15:13
Если вы тоже управляете сервером, base64 будет очень неэффективным, и его лучше избегать.
 Naresh13 нояб. 2012 г., 07:33
Поддерживает ли ваш сервер загрузку из нескольких частей?
 Hot Licks02 нояб. 2012 г., 17:03
мы никогда не видели фрагментированный конвертер Base64, но он не долженСлишком сложно взять реализацию с открытым исходным кодом и превратить ее в частичную. Base64 нетот сложно.
 Janub13 нояб. 2012 г., 07:54

Ответы на вопрос(2)

1. Кодировать большой файл в Base64 кусками

2. Загрузите этот файл

1. Кодируйте большой файл в Base64 кусками:

Найдите код, который кодируетNSData экземпляры для Base64. Тогда используйте одинNSInputStream читать данные кусками, кодировать их и использоватьNSOutputStream записать во временный файл. Я нашел этот вопрос / ответ:Возможно ли base64-кодировать файл кусками? Сделайте размер чанка кратным 3, и он должен работать. Вы можете переместить это в фоновый поток.

Важный: Это создаст временный файл, который больше, чем оригинал. На данный момент на устройстве должно быть достаточно места. После начала загрузки вы можете удалить его.

2. Загрузите этот файл

Кажется, ты уже сделал это. Вы можете использовать этоASIFormDataRequest так же, как в вашем примере. И поскольку он создает личную копию файла (он создает многочастный POST-файл), вы можете удалить свою копию.

Если вы хотите оптимизировать его путем распараллеливания этих шагов, вам нужно будет найти другой способ загрузки многокомпонентного файла.

Вы можете использовать поток ввода, чтобы сделать это Apple имеет некоторую информацию оВот

В основном вам нужно создать NSInputStream, указывающий на ваш файл, как в примере

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

Затем вы можете читать данные порциями, используя метод NSStreamDelegate - (void) stream: (NSStream *) stream handleEvent: (NSStreamEvent) eventCode

Снова Apple, приведите пример этого

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

Размер кусков, которые вы выбираете, должен быть сбалансирован между производительностью памяти и производительностью сети, поскольку очевидно, что с заголовками HTTP возникают некоторые накладные расходы

Вы также можете дополнительно оптимизировать, отправив свои NSData напрямую (и, следовательно, сохранить накладные расходы base64) проверить этот пост

Загрузка файла на HTTP-сервер в программировании iphone

Ваш ответ на вопрос