macos — Почему конечная точка NSURLSessionUploadTask для PHP с базовой аутентификацией отправляет данные дважды?

Я использую NSURLSessionUploadTask через NSURLSession в MacOS 10.12 Sierra с пользовательским делегатом для загрузки локального файла в Apache сервер с PHP скрипт, который запрашивает базовую аутентификацию. Это работает за исключением того, что задача загрузки появляется для отправки полных данных файла затем запрашивается сервером для NSURLAuthenticationChallenge и после отправки надлежащих учетных данных задача загрузки снова отправляет всю полезную нагрузку данных. Я ожидаю, что базовая проверка подлинности будет поставлена ​​перед загрузкой или что, если она произойдет после загрузки, что после подтверждения уже загруженные данные будут приняты, а не загружены во второй раз. Любая помощь в получении загруженных данных будет опубликована только один раз.

Скрипт конечной точки uploader.php:

<?php
$u = $_SERVER['PHP_AUTH_USER'];
$p = $_SERVER['PHP_AUTH_PW'];
if (($u !== 'user') || ($p !== 'password')) {
header('WWW-Authenticate: Basic realm="Restricted Area"');
header('HTTP/1.0 401 Unauthorized');
die('<h1>401 Unauthorized</h1>Access Denied.');
}
$response = 'file upload failed: upload not specified';
if (isset($_FILES['upload'])) {
$file_tmp_name = $_FILES['upload']['tmp_name'];
$file_name = $_FILES['upload']['name'];
$file_name_new = ('uploads/' . stripslashes($file_name));
if (!is_writable(dirname($file_name_new))) {
$response = 'file upload failed: directory is not writable.';
} else {
if (!move_uploaded_file($file_tmp_name, $file_name_new)) {
$response = 'file upload failed: couldn\'t move file to ' . $new_name;
} else {
$response = $file_name_new;
}
}
}
echo($response);
?>

FileUploader.m:

- (void)startUpload {
NSLog(@"starting upload");

NSURL *url = [NSURL URLWithString:@"https://www.domain.com/uploader.php"];
NSString *localPath = @"/path/to/file.ext";
NSString *inputName = @"upload";

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:30.0];
request.HTTPMethod = @"POST";
NSString *boundary = [NSString stringWithFormat:@"x-mime-boundary://%@", [NSUUID UUID].UUIDString];
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
[request setValue:contentType forHTTPHeaderField:@"Content-Type"];
[request setValue:[NSBundle mainBundle].bundleIdentifier forHTTPHeaderField:@"User-Agent"];

NSMutableData *postData = [NSMutableData data];
[postData appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; type=\"file\"; filename=\"%@\"\r\n\r\n", inputName, localPath.lastPathComponent] dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[NSData dataWithContentsOfFile:localPath]];
[postData appendData:[[NSString stringWithFormat:@"\r\n\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[request setValue:[NSString stringWithFormat:@"%ld", postData.length] forHTTPHeaderField:@"Content-Length"];

NSURLSessionConfiguration *defaultConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
self.session = [NSURLSession sessionWithConfiguration:defaultConfiguration delegate:self delegateQueue:[NSOperationQueue mainQueue]];
[[self.session uploadTaskWithRequest:request fromData:[NSData dataWithData:postData]] resume];
}

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
NSLog(@"URLSession didReceiveChallenge: %@", challenge.protectionSpace.authenticationMethod);
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
completionHandler(((credential) ? NSURLSessionAuthChallengePerformDefaultHandling : NSURLSessionAuthChallengeUseCredential), credential);
}
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
NSLog(@"NSURLSessionTask didReceiveChallenge: %@", challenge.protectionSpace.authenticationMethod);
NSString *username = @"user";
NSString *password = @"password";
NSURLCredential *credential = [NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceForSession];
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
NSLog(@"sent %ld b of %ld b (%.1f%%)", (long)totalBytesSent, (long)totalBytesExpectedToSend, (((float)totalBytesSent / (float)totalBytesExpectedToSend) * 100.0));
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
NSLog(@"upload complete");
if (error) [NSApp presentError:error];
[self.session invalidateAndCancel];
self.session = nil;
}

Сокращенный консольный вывод:

2016-10-11 12:14:34.323485 FileUploader[23624:5580925] starting upload
2016-10-11 12:14:34.429419 FileUploader[23624:5580925] URLSession didReceiveChallenge: NSURLAuthenticationMethodServerTrust
2016-10-11 12:14:34.459239 FileUploader[23624:5580925] sent 32768 b of 10616647 b (0.3%)
2016-10-11 12:14:34.459351 FileUploader[23624:5580925] sent 65536 b of 10616647 b (0.6%)
...
2016-10-11 12:14:42.849080 FileUploader[23624:5580925] sent 10584064 b of 10616647 b (99.7%)
2016-10-11 12:14:42.849179 FileUploader[23624:5580925] sent 10616647 b of 10616647 b (100.0%)
2016-10-11 12:14:43.038092 FileUploader[23624:5580925] NSURLSessionTask didReceiveChallenge: NSURLAuthenticationMethodHTTPBasic
2016-10-11 12:14:43.040085 FileUploader[23624:5580925] sent 10649415 b of 21233294 b (50.2%)
2016-10-11 12:14:43.040141 FileUploader[23624:5580925] sent 10682183 b of 21233294 b (50.3%)
...
2016-10-11 12:14:46.508339 FileUploader[23624:5580925] sent 21200711 b of 21233294 b (99.8%)
2016-10-11 12:14:46.594864 FileUploader[23624:5580925] sent 21233294 b of 21233294 b (100.0%)
2016-10-11 12:14:46.757213 FileUploader[23624:5580925] upload complete

0

Решение

Есть несколько способов решить эту проблему. Первые два, которые приходят на ум:

  • Перед отправкой запроса POST для проверки учетных данных отправьте явный запрос HEAD или GET. Это будет работать> 99% времени.
  • Вместо отправки кода ошибки аутентификации отправьте большой двоичный объект JSON, который ваше приложение может распознать как ошибку, и в этом большом двоичном объекте JSON укажите UUID для предыдущей загрузки, которую ваше приложение затем может предоставить в новом запросе, чтобы связать загрузку с учетная запись пользователя. Это будет работать 100% времени, но вам нужно будет добавить на сервер задание cron для периодического удаления старых файлов.

Любой подход включает изменения на стороне клиента и на стороне сервера.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]