10

我試圖使用多部分表單數據使用- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL; 方法上傳視頻/圖像文件。但不知何故,我無法上傳文件,我得到「stream ended unexpectedly」錯誤。無法使用iOS中的NSURLSession多部分表單數據上傳文件

要求

  1. 上傳的視頻/圖像文件到服務器
  2. 應用程序應該支持後臺上傳(繼續應用進入後臺即使在上傳過程)
  3. 服務器預計該數據將使用多部分表單數據發送。

方法/ API的使用來實現這一

  1. NSURLSession背景會話API(下面列出完整代碼)

    2. - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL

挑戰/問題面臨着

  1. 我使用這個API上傳過程中每一次

點要注意

  1. 上傳越來越成功的代碼相同面臨着「stream ended unexpectedly」錯誤如果我使用NSURLConnection而不是NSURLSession

  2. NSURLSession後臺上傳過程預計文件位置(NSURL)爲參數,不接受NSData。它不允許我們在上傳之前將文件轉換爲NSData,即我們無法將NSData添加到文件正文。

需要幫助的以下幾點

  1. 是否有正在形成的多FORMDATA身體的任何錯誤(注 - 同樣的代碼與NSURLConnection的工作)

  2. 我的方法在哪裏出錯?

  3. 我們是否需要在服務器級別進行任何更改以支持NSURLSession backgroundSession上傳? (在數據解析或其他東西?)

    這裏是被用於上載文件

的NSString * BoundaryConstant = @ 「---------- V2ymHFg03ehbqgZCaKO6jy」 的代碼;

// string constant for the post parameter 'file'. My server uses this name: `file`. Your's may differ 
    NSString* FileParamConstant = @"file"; 

    // the server url to which the image (or video) is uploaded. Use your server url here 

    url=[NSURL URLWithString:[NSString stringWithFormat:@"%@%@%d",baseURL,@"posts/post/update/",createPostObject.PostID]];  


    // create request 
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; 
    [request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData]; 
    [request setHTTPShouldHandleCookies:NO]; 
    [request setTimeoutInterval:120]; 
    [request setHTTPMethod:@"POST"]; 
    [request addValue:@"multipart/form-data" forHTTPHeaderField:@"Content-Type"]; 

    [request setURL:url]; 

    // set Content-Type in HTTP header 
    NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", BoundaryConstant]; 
    [request setValue:contentType forHTTPHeaderField: @"Content-Type"]; 

    if([[NSUserDefaults standardUserDefaults] objectForKey:@"accessToken"]){ 

     [request setValue:[[NSUserDefaults standardUserDefaults] objectForKey:@"accessToken"] forHTTPHeaderField:AccessTokenKey]; 

    } 

    // post body 
    NSMutableData *body = [NSMutableData data]; 

    // add params (all params are strings) 
    for (NSString *param in self.postParams) { 

     NSLog(@"param is %@",param); 

     [body appendData:[[NSString stringWithFormat:@"--%@\r\n", BoundaryConstant] dataUsingEncoding:NSUTF8StringEncoding]]; 
     [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", param]    dataUsingEncoding:NSUTF8StringEncoding]]; 
     [body appendData:[[NSString stringWithFormat:@"%@\r\n", [self.postParams objectForKey:param]] dataUsingEncoding:NSUTF8StringEncoding]]; 
    } 

    // add video file name to body 

     [body appendData:[[NSString stringWithFormat:@"--%@\r\n", BoundaryConstant] dataUsingEncoding:NSUTF8StringEncoding]]; 
     [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"file.mp4\"\r\n", FileParamConstant] dataUsingEncoding:NSUTF8StringEncoding]]; 
     [body appendData:[[NSString stringWithString:@"Content-Type: video/mp4\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; 
     // [body appendData:self.dataToPost]; 
     [body appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; 

     [body appendData:[[NSString stringWithFormat:@"--%@--\r\n", BoundaryConstant] dataUsingEncoding:NSUTF8StringEncoding]]; 



    // setting the body of the post to the request 
    [request setHTTPBody:body]; 

    // set the content-length 
    NSString *postLength = [NSString stringWithFormat:@"%lu", (unsigned long)[body length]]; 
    [request setValue:postLength forHTTPHeaderField:@"Content-Length"]; 

    NSLog(@"Request body %@", [[NSString alloc] initWithData:[request HTTPBody] encoding:NSUTF8StringEncoding]); 

    NSURLSessionConfiguration * backgroundConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"backgroundtask1"]; 

    NSURLSession *backgroundSeesion = [NSURLSession sessionWithConfiguration: backgroundConfig delegate:self delegateQueue: [NSOperationQueue mainQueue]]; 


    NSURLSessionUploadTask *uploadTask = [backgroundSeesion uploadTaskWithRequest:request fromFile:self.videoUrl]; 
    [uploadTask resume]; 
+0

你似乎設置「內容 - 鍵入「兩次。不知道如何解決。不知道其餘的。我做了類似的事情,但是單獨創建上傳緩衝區(在C++函數中)並添加空洞緩衝區。所以,我不需要更多的貢獻。 –

回答

9

您沒有上傳自己認爲的東西。您的意圖是讓身體數據按原樣上傳。相反,當您撥打uploadTaskWithRequest:fromFile:時,該方法會有效地清除請求中的任何HTTPBodyHTTPBodyStream值,並將其替換爲您通過fromFile:參數傳入的URL的內容。

因此,除非您將表單編碼的主體數據塊寫入其他地方的該文件URL,否則您將自行上傳文件而不是多部分表單數據。

您需要調整代碼以將表單數據寫入文件,而不是將其存儲在HTTPBody中,然後將該文件的URL傳遞給參數fromFile:

+0

請你能提供一個代碼片斷,謝謝。 –

+0

1.從文件:改爲fromData:,並傳遞'body'的值。 – dgatwood

+0

2.取消註釋// [body appendData:self.dataToPost];並將self.dataToPost更改爲[NSData dataWithContentsOfURL:self.videoURL]。 – dgatwood

4

爲了防止浪費時間處理它。

基於@dgatwood答案的完整片段

private func http(request: URLRequest){ 
     let configuration = URLSessionConfiguration.default 
     let session = URLSession(configuration: configuration, delegate: self, delegateQueue: .main) 
     /*Tweaking*/ 
     let task = session.uploadTask(with: request, from: request.httpBody!) 
     task.resume() 
    } 

和..不要忘了添加請求對象的頭就像

request.setValue("multipart/form-data; boundary=\(yourboundary)", forHTTPHeaderField: "Content-Type") 
相關問題