(一二八)使用POST上傳文件

簡介

上傳文件到服務器是一個比較常用的操作,最基本的方式是通過POST上傳,文件以二進制形式,作爲一個參數傳遞,但是這個POST的結構相當複雜,且必須完全符合HTTP標準。

文件上傳的POST格式

該POST主要由下面幾個部分構成。

  • 請求頭
    1.Content-Length(請求體的二進制大小)

    • 注意這裏的二進制大小應該根據請求體計算

    2.Content-Type multipart/form-data; boundary=(分隔符)

    • 注意這裏的分隔符與請求體的分隔符有關,但不完全一致。
  • 請求體
    基本格式示例

    • 這裏的分隔符就是上面的分隔符,但是前面要多加兩個'-'
    --分隔符
    Content-Disposition: form-data; name="uploadFile"; filename="button.png"
    Content-Type: image/png
    (此處空兩行)
    <二進制內容>
    --分隔符
    Content-Disposition: form-data; name="submit"
    (此處空兩行)
    Submit
    --分隔符--
    (此處空一行)
    • 注意到最後的分隔符後面跟了--,這個代表結束符,並且後面要跟一個空行。

    格式說明

    • 普通參數
      普通參數的構成如下:

      Content-Disposition: form-data; name="參數名"
      (此處空兩行)
      參數值
      --分隔符    

      多個參數可以連續拼接。

    • 文件參數
      文件參數與普通參數類似,只是多了一行MineType的說明,該說明告訴服務器文件的類型。

      Content-Disposition: form-data; name="uploadFile"; filename="button.png"
      Content-Type: image/png
      (此處空兩行)
      <二進制內容>
      --分隔符

    將二者連起來就構成了完整的文件上傳POST信息,到這裏我們可以理解,該POST不僅發送了文件數據,還發送了一個參數。

文件上傳的服務器php腳本

  • 要讓post請求發揮作用,必須藉助php腳本實現對post的處理,換句話說,我們的post請求應該發送給該php腳本,腳本的代碼如下:(注意修改uploadPath爲自己服務器想要接收文件的路徑)

    <?php
    header("Content-type: text/html; charset=utf-8"); 
    // 配置文件需要上傳到服務器的路徑,需要允許所有用戶有可寫權限,否則無法上傳!
    $uploadPath = '../uploads/';
    
    $IOS_forKey=$_FILES["uploadFile"];
    
    if ($IOS_forKey["error"] > 0) {
        echo "傳入參數錯誤:" . $IOS_forKey["error"] . "<br />";
    } else {
        echo "文件: " . $IOS_forKey["name"] . "<br />";
        echo "類型: " . $IOS_forKey["type"] . "<br />";
        echo "大小: " . ($IOS_forKey["size"] / 1024) . " Kb<br />";
        echo "臨時文件: " . $IOS_forKey["tmp_name"] . "<br />";
    
        chmod($uploadPath . $IOS_forKey["name"], 0666);
        if (file_exists($uploadPath . $IOS_forKey["name"])) {
            echo $IOS_forKey["name"] . "文件已經存在!";
        } else {
            move_uploaded_file($IOS_forKey["tmp_name"], $uploadPath . $IOS_forKey["name"]);
            echo "上傳文件保存在: " . $uploadPath . $IOS_forKey["name"];
        }
    }
    ?>

通過iOS設備上傳文件

想要通過iOS設備上傳文件,一般的做法是根據上面的結構創建URLRequset,然後發送該request到服務器請求上面的php腳本,實現文件的上傳,具體的代碼如下。

  • 爲了方便插入二進制文件數據,我們直接使用data拼接,因此對於每一段字符串都需要轉爲data,這就是DataWithStr宏的作用。

  • 爲了適配各個系統的換行符,使用\r\n。

  • FileBoundary就是上文提到的分隔符。

  • HTTP請求頭的contentLength需要待請求體拼接完畢後才能得到,因此最後才賦值。

#import "ViewController.h"
#import "UploadFile.h"

#define FileBoundary @"-----------------------------test"
#define EndLine @"-----------------------------test--\r\n"
#define NewLine @"\r\n"
#define DataWithStr(str) [str dataUsingEncoding:NSUTF8StringEncoding]

@interface ViewController ()

@end

@implementation ViewController

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

    [self upload];

}

- (void)upload{

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://127.0.0.1/lesson2/upload.php"]];
    request.HTTPMethod = @"POST";
    // 設置請求頭
    NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",FileBoundary];
    [request setValue:contentType forHTTPHeaderField:@"Content-Type"];
    // 設置請求體
    NSMutableData *body = [NSMutableData data];
    [body appendData:DataWithStr(@"--")];
    [body appendData:DataWithStr(FileBoundary)];
    [body appendData:DataWithStr(NewLine)];
    [body appendData:DataWithStr(@"Content-Disposition: form-data; name=\"uploadFile\"; filename=\"test.png\"")];
    [body appendData:DataWithStr(NewLine)];
    [body appendData:DataWithStr(@"Content-Type: image/png")];
    [body appendData:DataWithStr(NewLine)];
    [body appendData:DataWithStr(NewLine)];

    UIImage *img = [UIImage imageNamed:@"test.png"];
    NSData *imgData = UIImagePNGRepresentation(img);
    [body appendData:imgData];
    [body appendData:DataWithStr(NewLine)];

    // 其他參數
    [body appendData:DataWithStr(@"--")];
    [body appendData:DataWithStr(FileBoundary)];
    [body appendData:DataWithStr(@"Content-Disposition: form-data; name=\"param1\"")];
    [body appendData:DataWithStr(NewLine)];
    [body appendData:DataWithStr(NewLine)];
    [body appendData:DataWithStr(@"value1")];
    [body appendData:DataWithStr(NewLine)];
    [body appendData:DataWithStr(@"--")];
    [body appendData:DataWithStr(EndLine)];

    [request setValue:[NSString stringWithFormat:@"%ld",body.length] forHTTPHeaderField:@"Content-Length"];

    request.HTTPBody = body;

    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"%@",result);
    }];

}

@end
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章