NSURLSession/NSURLConnection的上傳檔案方法,nsurlsession上傳圖片

來源:互聯網
上載者:User

NSURLSession/NSURLConnection的上傳檔案方法,nsurlsession上傳圖片

最好的學習方法就是 領悟 + 證悟。

此篇文章的理論基礎主要是與HTTP網路通訊協定相關。為集中精力,可以先把TCP/IP協議這些置之不理,也就是先只關注HTTP的請求和響應的結構。HTTP完整的原理內容就此略過。在此只略提相關內容。文中涉及的設計源碼可以通過這裡擷取 https://github.com/wuqingjian2015/uploadHelper,有意者可以去看看。

 

HTTP是幹什麼用的呢?

先考慮一下以下應用過程:

那麼,如果應用上面過程來實現上傳檔案這個功能,需要做到幾方面:

HTTP協議就是解決以上這些問題的。它定義了請求體結構和響應體結構。只要用戶端或服務端遵守這個標準,它就能與任何遵守這一標準的應用程式通訊。

如果再想實地觀察一下符合HTTP標準的請求體和響應體“長”什麼樣,可以用一些抓包工具。我用了Wireshark和Charles。如果你的是網頁應用,可以在IE上按F12鍵調出開發工具視窗的網路Tab。

在這裡,我們只關注請求,瞭解響應StatusCode是200表示正常。

對於請求,因為iOS會自動化佈建其他內容,如果咱們不設定的話。下面只討論其中的

如何設定目標地址?在建立NSURLRequest時,指定URL即可。如,

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:self.targetURL];

接下來,我們需要設定Content-Type的值為:multipart/form-data,同時制定boundary的值,該boundary會在佈建要求本文時用到。到此為止,我們得到了這樣的一些代碼:

-(NSURLRequest *)createRequestHeader

{

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:self.targetURL]; //指定目標地址

    //建立http header請求標題內容

    //  Content-Type := multipart/form-data; boundary=---------------827292(任意)

    //  Content-Length := (檔案長度)

    NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary];

    [request setValue:contentType forHTTPHeaderField:@"Content-Type"]; //設定Content-Type

    [request setHTTPMethod:@"post"];//設定Method為POST

    return request;

}

 下面再來看請求本文怎麼設定。在iOS中,由NSURLRequest.HTTPBody屬性來指定,其為NSData類型。謹記:這個有固定的格式,該格式必須正確,否則伺服器端無法取得正確的內容。而這個問題無法通過抓包工具中體現出來。如下:

 格式:

beginBoundary

Content-Disposition: form-data; name="<伺服器端需要知道的名字>"; filename="<伺服器端這個傳上來的檔案名稱>"
Content-Type: application/zip --根據不同的檔案類型選擇不同的值

<空行> 

<位元據>

endBoundary

範例:

----KenApp299912318
Content-Disposition: form-data; name="<伺服器端需要知道的名字>"; filename="<伺服器端這個傳上來的檔案名稱>"
Content-Type: application/zip --根據不同的檔案類型選擇不同的值

<空行>

<位元據>

----KenApp299912318--

有代碼有真相:

-(NSData*)createDataForRequestHTTPBodyForSource

{

    NSMutableString *bodyHead = [[NSMutableString alloc] init];

    NSMutableData *data = [[NSMutableData alloc] init];

    

    NSString *fileName = [self.sourceURL lastPathComponent];

    NSString *name=@"uploadFile";

    

    NSData *fileContent = [NSData dataWithContentsOfURL:self.sourceURL];

    //建立http body請求體內容

    //  第一行: --827292

    [bodyHead appendString:self.beginBoundary];

    // [body appendFormat:@"--------------------"]

    //  Content-Disposition: form-data; name="uploadFile"; filename="xxxx.ext"

    [bodyHead appendFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n",name,fileName];

    //  Content-Type: application/x-zip-compressed

    //  (空行)

    [bodyHead appendFormat:@"Content-Type: application/zip\r\n\r\n"];

    //  (位元據)

    [data appendData:[bodyHead dataUsingEncoding:NSUTF8StringEncoding]];

    [data appendData:fileContent];

    //  最後一行:827292--

    

    [data appendData:[self.endBoundary dataUsingEncoding:NSUTF8StringEncoding]];

    

    return data;

}

 到目前為止,咱們知道怎麼佈建要求標題和請求本文了。怎麼用上這些結果呢?

如果是用NSURLConnection的話, 我們需要在同一個NSURLRequest中設定好這兩者。

再調用factory method [NSURLConnection connectionWithRequest: delegate:];

如果是用NSURLSession中uploadTask的話,需要在NSURLRequest中佈建要求標題(如下requestWithHeader),同時在NSData中佈建要求本文(如下requestHTTPBody)。代碼例子如下,其中SSUploadHelper封裝了以上提到的處理過程。

/////////////////////////////////範 例////////////////////////

  SSUploadHelper *uploadHelper = [[SSUploadHelper alloc] initWithTarget:[NSURL URLWithString:@"http://192.168.31.172:5012/ArchFlow/upload"] forSource:self.downloadedLocation];

    NSURLSessionUploadTask *uploadTask = [self.ephemeralSession uploadTaskWithRequest:[uploadHelper requestWithHeader] fromData:[uploadHelper requestHTTPBody] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

        

        NSLog(@"Got response %@ with error %@.\n", response, error);

        NSLog(@"DATA:\n%@\nEND DATA\n",

              [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);

    }];

    

    [uploadTask resume];

//////////////////////////////////

至此,用戶端的設計基本完成了。為了在伺服器端看到上傳到的檔案,咱們需要搭建一個伺服器環境了。我個人實現了一個基於Python的REST的微伺服器,在處理到ArchFlow/upload的POST請求中,從request中擷取檔案,並儲存到本地目錄下。這是我在軟體架構時用到的工具伺服器,在此基礎上作的臨時上傳檔案功能。

//功能測試:

在伺服器啟動的過程中,執行以上用戶端代碼,可以看到檔案被拷貝到目標目錄下。

注意事項:

boundary的格式值得加倍注意,在請求標題中指明的boundary,必須用到請求本文中。

剩下的就是耐心調試了。Good Luck!

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.