Незавершенные загрузки файлов с использованием ASIHTTPRequest и Rackspace Cloud Files - PullRequest
1 голос
/ 05 марта 2012

Я загружаю mp3-файлы из облачных файлов Rackspace, и для больших файлов я сталкиваюсь с проблемой, когда загрузка успешно завершена, но файл еще не загружен полностью. Например, mp3-файл 40 MB (длительность 01:00:00) загружается как mp3-файл 4.5 MB (длительность 00:10:30). Это не происходит все время.

  1. Есть какие-нибудь указатели относительно того, что происходит?
  2. Почему это происходит, и как я могу исправить эту проблему?
  3. Как создать простую логику контрольной суммы, чтобы проверить, был ли файл загружен полностью?

Вот как я могу создать и отправить асинхронный запрос:

ASIHTTPRequest *request;
request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:urlString]];
[request setShouldAttemptPersistentConnection:NO]; 
[request setAllowResumeForFileDownloads:YES];
[request setDownloadProgressDelegate:self];
[request setShouldContinueWhenAppEntersBackground:YES];
[request setUserInfo:userInfo];
[request setDownloadDestinationPath:downloadPath];
[request setTemporaryFileDownloadPath:[NSString stringWithFormat:@"%@.download", downloadPath]];

[self.networkQueue addOperation:request];
[self.networkQueue go];

Обратите внимание, я использую сетевую очередь с 4 одновременными загрузками.

Спасибо.

Редактировать (пн 5 марта 2012, 15:25)

Итак, дальнейшее исследование показывает, что ASINetworkQueue вызывает requestDidFinishSelector метод делегата вместо requestDidFailSelector. Код состояния, возвращаемый объектом ASIHTTPRequest, - 206, HTTP/1.1 206 Partial Content в методе requestDidFinishSelector. Код состояния должен быть 200, HTTP/1.1 200 OK.

Я до сих пор не знаю почему! и я до сих пор не знаю, как это исправить. Кажется, мне придется удалить частично загруженный файл и начать процесс загрузки снова. На этом этапе временный файл, т.е. %@.download, удаляется, и этот частично загруженный файл помещается в путь назначения.

1 Ответ

1 голос
/ 07 марта 2012

Итак, это то, что я в итоге сделал, и, надеюсь, этого будет достаточно (для решения проблемы).

Вот как я создаю сетевую очередь:

- (ASINetworkQueue *)networkQueue {

    if (!_networkQueue) {
        _networkQueue = [[ASINetworkQueue alloc] init];
        [_networkQueue setShowAccurateProgress:YES];
        [_networkQueue setRequestDidFinishSelector:@selector(contentRequestDidSucceed:)];
        [_networkQueue setRequestDidFailSelector:@selector(contentRequestDidFail:)];
        [_networkQueue setShouldCancelAllRequestsOnFailure:NO];
        [_networkQueue setDelegate:self];
    }

    return _networkQueue;
}

И вот что делает мой contentRequestDidSucceed: метод:

- (void)contentRequestDidSucceed:(ASIHTTPRequest *)request {
    // ASIHTTPRequest doesn't use HTTP status codes (except for redirection), 
    // so it's up to us to look out for problems (ex: 404) in the requestDidFinishSelector selector. 
    // requestDidFailSelector will be called only if there is the server can not be reached 
    // (time out, no connection, connection interrupted, ...)

    // In certain cases ASIHTTPRequest/ASINetworkQueue calls the delegate method requestDidFinishSelector,
    // instead it should call requestDidFailSelector. I've encountered this specific case with status code 206 (HTTP/1.1 206 Partial Content). In this case the file was not completely downloaded, so we'll have to re-process the request.
    if ([request responseStatusCode] != 200) {
        NSLog(@" ");
        NSLog(@"======= BEEP =======");
        NSLog(@" ");

        // We're double checking that the file was indeed not downloaded completely!
        // During internal testing, we encountered a case where download was successful
        // but we received 206 as the response code (maybe we received the cached value).
        unsigned long long progress = [request totalBytesRead] + [request partialDownloadSize];
        unsigned long long total = [request contentLength] + [request partialDownloadSize];

        if (progress != total) {
            NSString *downloadPath = [request downloadDestinationPath];
            NSString *temporaryDownloadPath = [self temporaryPathForFile:downloadPath];

            // Move the file at destination path to the temporary destination path (back again)    
            NSError *moveError = nil;
            [[[[NSFileManager alloc] init] autorelease] moveItemAtPath:downloadPath 
                                                                toPath:temporaryDownloadPath 
                                                                 error:&moveError];

            if (moveError) {
                NSLog(@"Failed to move file from '%@' to '%@'", downloadPath, temporaryDownloadPath);

                NSError *removeError = nil;
                [ASIHTTPRequest removeFileAtPath:downloadPath error:&removeError];

                if (removeError) {
                    NSLog(@"Failed to remove file from '%@'", downloadPath);
                }
            }

            // Call the requestDidFailSelector method
            [self contentRequestDidFail:request];

            // Don't continue
            return;   
        }        
    }

    // TODO: Process successful request!
    // . . .
}

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

...