NSURLSession上傳的基本用法

NSURLSession上傳文件

(一)上傳單個文件的過程及原理總結(jié)

1.上傳文件需要注意兩點

第一點 : 請求頭里面的Content-Type
  • 請求頭里面Content-Type的作用 : 告訴服務(wù)器我的客戶端的請求是干什么的,是做普通請求還是做文件上傳

  • 普通請求的Content-Type : Content-Type: application/x-www-form-urlencoded

  • 文件上傳的Content-Type : Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryf3vccsnJe8EBoxHY

  • boundary : 表示文件上傳時的文件分隔符;可以自定義;自定義分隔符時,前面四個中劃線可以省略,分隔符不能有中文;

  • 自定義boundary : boundary=zxc

  • 自定義boundary之后的Content-Type : Content-Type:multipart/form-data; boundary=zxc

第二點 : 請求體
------WebKitFormBoundaryf3vccsnJe8EBoxHY
Content-Disposition: form-data; name="userfile"; filename="car.jpg"
Content-Type: image/jpeg


------WebKitFormBoundaryf3vccsnJe8EBoxHY--
請求體解釋 :
  • 第一行 : 文件的起始分割符,分割文件用的.以"--"開頭+分隔符(----WebKitFormBoundaryf3vccsnJe8EBoxHY),分隔符前面的"----"可以省略,比如--zxc
  • 第二行 : Content-Disposition,文件上傳時需要處理的表單數(shù)據(jù).
    • name="userfile" : 服務(wù)器接收文件的字段名,由服務(wù)器提供,并告知前端開發(fā)者.
    • filename="car.jpg" : 文件保存到服務(wù)器上的文件名.可以自定義也可以直接使用文件的原始文件名
  • 第三行 : Content-Type: image/jpeg,告訴服務(wù)器上傳的文件類型.如果不想告訴服務(wù)器文件的類型,可以使用Content-Type: application/octet-stream
  • 第四行 : 單純的回車換行 \r\n
  • 第五行 : 上傳的文件的二進制數(shù)據(jù)data
  • 第六行 : 整個文件結(jié)束的分隔符,以"--"開頭,以"--"結(jié)尾,兩個"--"都不能少.比如--zxc--
代碼實現(xiàn)文件上傳需要做的事情 :
  • 1.設(shè)置請求頭 Content-Type: multipart/form-data; boundary=zxc

  • 2.設(shè)置請求體,需要拼接請求體中的字符串和二進制數(shù)據(jù)

    • 請求體數(shù)據(jù)結(jié)構(gòu)
    --zxc\r\n
    Content-Disposition: form-data; name="userfile"; filename="car.jpg"\r\n
    Content-Type: image/jpeg\r\n
    \r\n
    data
    \r\n--zxc--
    

2.說明

  • boundary : 表示文件的分隔符,分界線.非中文的字符組成.
  • userfile:服務(wù)器字段名,開發(fā)的時候,可以咨詢后服務(wù)器端程序員.
  • filename:將文件保存在服務(wù)器上的文件名稱.
  • Content-Type:客戶端告訴服務(wù)器上傳文件的文件類型.
    • text/plain
    • image/jpg
    • image/png
    • image/gif
    • text/html
    • application/json
    • application/octet-stream(8進制流)贼涩,如果不想告訴服務(wù)器具體的文件類型菲驴,可以使用這個 Content-Type.
  • 注意:每一行末尾一定需要有 \r\n.

(二)單個文件上傳實現(xiàn)

1.準備要上傳的數(shù)據(jù),調(diào)用文件上傳主方法

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    // 參數(shù)1
    NSString *URLString = @"http://localhost/php/upload/upload.php";
    // 參數(shù)2
    NSString *serverFileName = @"userfile";
    // 參數(shù)3
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"mm01.jpg" ofType:nil];

    // 調(diào)用文件上傳主方法
    [self uploadFileWithURLString:URLString serverFileName:serverFileName filePath:filePath];
}

2.文件上傳主方法

/**
 *  文件上傳主方法
 *
 *  @param URLString      文件上傳地址
 *  @param serverFileName 服務(wù)器接收文件的字段名,開發(fā)中由服務(wù)器那邊提供
 *  @param filePath       文件路徑,有了路徑就可以獲取到文件二進制數(shù)據(jù)和文件名
 */
- (void)uploadFileWithURLString:(NSString *)URLString serverFileName:(NSString *)serverFileName filePath:(NSString *)filePath
{
    // URL
    NSURL *URL= [NSURL URLWithString:URLString];

    // 可變請求
    NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:URL];
    // 設(shè)置請求頭信息
    [requestM setValue:@"multipart/form-data; boundary=itcast" forHTTPHeaderField:@"Content-Type"];
    // 設(shè)置請求方法
    requestM.HTTPMethod = @"POST";
    // 設(shè)置請求
    requestM.HTTPBody = [self getHTTPBodyWithServerFileName:serverFileName filePath:filePath];

    // 發(fā)送請求實現(xiàn)圖片上傳
    [[[NSURLSession sharedSession] dataTaskWithRequest:requestM completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

        // 處理響應(yīng)
        if (error == nil && data != nil) {
            // 反序列化
            NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
            NSLog(@"%@",result);
        } else {
            NSLog(@"%@",error);
        }

    }] resume];
}

3.獲取請求體信息

/**
 *  獲取請求體信息
 *
 *  @param serverFileName 服務(wù)器接收文件的字段名
 *  @param filePath       文件路徑
 *
 *  @return 返回請求體二進制信息
 */
- (NSData *)getHTTPBodyWithServerFileName:(NSString *)serverFileName filePath:(NSString *)filePath
{
    // 定義dataM,拼接請求體的二進制信息
    NSMutableData *dataM = [NSMutableData data];

    // 拼接文件二進制前面的字符串
    NSMutableString *stringM = [NSMutableString string];

    // 拼接文件開始的分隔符
    [stringM appendString:@"--itcast\r\n"];
    // 拼接表單數(shù)據(jù)
    [stringM appendFormat:@"Content-Disposition: form-data; name=%@; filename=%@\r\n",serverFileName,[filePath lastPathComponent]];
    // 拼接文件類型
    [stringM appendString:@"Content-Type: image/jpeg\r\n"];
    // 拼接單純的換行
    [stringM appendString:@"\r\n"];
    // 把這部分的字符串轉(zhuǎn)成二進制,拼接到dataM里面
    [dataM appendData:[stringM dataUsingEncoding:NSUTF8StringEncoding]];

    // 拼接文件的二進制數(shù)據(jù)
    [dataM appendData:[NSData dataWithContentsOfFile:filePath]];

    // 拼接文件結(jié)束的分隔符
    NSString *end = @"\r\n--itcast--";
    [dataM appendData:[end dataUsingEncoding:NSUTF8StringEncoding]];

    return dataM.copy;
}

(三)多個文件上傳原理分析

  • 原理 : 循環(huán)的將多個文件拼接起來,并用上傳文件的分隔符分割開.

1.上傳文件需要注意兩點

第一點 : Content-Type
  • 第一點 : Content-Type,告訴服務(wù)器是發(fā)送文件.
    • Content-Type: multipart/form-data; boundary=----WebKitFormBoundary0YVF2TB5DWTxe
第二點 : 請求體
  • 請求體數(shù)據(jù)結(jié)構(gòu)
--itcast\r\n
 Content-Disposition: form-data; name="userfile[]"; filename="mm01.jpg"\r\n
 Content-Type: image/jpeg\r\n
 \r\n
 data\r\n
 --itcast\r\n
 Content-Disposition: form-data; name="userfile[]"; filename="mm02.jpg"\r\n
 Content-Type: image/jpeg\r\n
 \r\n
 data\r\n
 --itcast\r\n
 Content-Disposition: form-data; name="status"\r\n
 \r\n
 今天和女神的男神在一起!好開心!\r\n
 --itcast--

2.說明

  • 第一個文件開始分隔符前有無\r\n是沒有影響的.
  • 上傳多個文件時的請求體需要把多個文件循環(huán)的拼接起來.
  • 有些服務(wù)器可以在上傳文件的同時,提交一些文本內(nèi)容給服務(wù)器.典型應(yīng)用 : 新浪微博,上傳圖片的同時,發(fā)送一個微博.
  • name="status"中的"status" 是腳本文件接收上傳時文字信息的參數(shù)的名稱

(四)多個文件上傳實現(xiàn)

1.準備上傳數(shù)據(jù),調(diào)用多文件上傳的主方法

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    // 參數(shù)1
    NSString *URLString = @"http://localhost/php/upload/upload-m.php";
    // 參數(shù)2
    NSString *serverFileName = @"userfile[]";
    // 參數(shù)3
    NSString *filePath1 = [[NSBundle mainBundle] pathForResource:@"mm01.jpg" ofType:nil];
    NSString *filePath2 = [[NSBundle mainBundle] pathForResource:@"mm02.jpg" ofType:nil];
    NSArray *filePaths = @[filePath1,filePath2];
    // 參數(shù)4
    NSDictionary *textDict = @{@"status":@"今天和女神的男神在一起!好開心!"};

    // 調(diào)用文件上傳的主方法
    [self uploadFilesWithURLString:URLString serverFileName:serverFileName filePaths:filePaths textDict:textDict];
}

2.多文件上傳的主方法

/**
 *  多文件上傳的主方法
 *
 *  @param URLString      文件上傳的地址
 *  @param serverFileName 服務(wù)器接收文件的字段名
 *  @param filePaths      文件路徑的數(shù)組
 *  @param textDict       文件上傳的附帶信息
 */
- (void)uploadFilesWithURLString:(NSString *)URLString serverFileName:(NSString *)serverFileName filePaths:(NSArray *)filePaths textDict:(NSDictionary *)textDict
{
    // URL
    NSURL *URL = [NSURL URLWithString:URLString];

    // 可變請求
    NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:URL];
    // 設(shè)置請求頭
    [requestM setValue:@"multipart/form-data; boundary=itcast" forHTTPHeaderField:@"Content-Type"];
    // 設(shè)置請求方法
    requestM.HTTPMethod = @"POST";
    // 設(shè)置請求體
    requestM.HTTPBody = [self getHTTPBodyWithServerFileName:serverFileName filePaths:filePaths textDict:textDict];

    // 發(fā)送請求實現(xiàn)文件上傳
    [[[NSURLSession sharedSession] dataTaskWithRequest:requestM completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

        // 處理響應(yīng)
        if (error == nil && data != nil) {

            // 反序列化
            id result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
            NSLog(@"%@",result);

        } else {
            NSLog(@"%@",error);
        }
    }] resume];
}

3.獲取請求體信息

/**
 *  獲取請求體信息
 *
 *  @param serverFileName 服務(wù)器接收文件的字段名
 *  @param filePaths      文件的路徑數(shù)組
 *  @param textDict       文件上傳時的文本信息
 *
 *  @return 文件的二進制數(shù)據(jù)
 */
- (NSData *)getHTTPBodyWithServerFileName:(NSString *)serverFileName filePaths:(NSArray *)filePaths textDict:(NSDictionary *)textDict
{
    // 定義dataM拼接請求體二進制數(shù)據(jù)
    NSMutableData *dataM = [NSMutableData data];

    // 循環(huán)拼接文件二進制信息
    [filePaths enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        // 用于字符串信息
        NSMutableString *stringM = [NSMutableString string];
        // 拼接文件開始的分隔符
        [stringM appendString:@"--itcast\r\n"];
        // 拼接表單數(shù)據(jù)
        [stringM appendFormat:@"Content-Disposition: form-data; name=%@; filename=%@\r\n",serverFileName,[obj lastPathComponent]];
        // 拼接文件類型
        [stringM appendString:@"Content-Type: image/jpeg\r\n"];
        // 拼接單純的換行
        [stringM appendString:@"\r\n"];
        // 把前面的字符串信息拼接到請求體里面
        [dataM appendData:[stringM dataUsingEncoding:NSUTF8StringEncoding]];

        // 拼接文件的二進制數(shù)據(jù)到dataM
        [dataM appendData:[NSData dataWithContentsOfFile:obj]];

        // 拼接二進制數(shù)據(jù)后面的換行
        [dataM appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];

    }];

    // 拼接文件的文本信息
    [textDict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        // 用于拼接文本信息
        NSMutableString *stringM = [NSMutableString string];
        // 拼接文本信息的開始分割符
        [stringM appendString:@"--itcast\r\n"];
        // 拼接表單數(shù)據(jù)
        [stringM appendFormat:@"Content-Disposition: form-data; name=%@\r\n",key];
        // 拼接單純的換行
        [stringM appendString:@"\r\n"];
        // 拼接文本信息
        [stringM appendFormat:@"%@\r\n",obj];

        // 把文本信息拼接到請求體
        [dataM appendData:[stringM dataUsingEncoding:NSUTF8StringEncoding]];
    }];

    // 拼接文件上傳的結(jié)束分隔符
    [dataM appendData:[@"--itcast--" dataUsingEncoding:NSUTF8StringEncoding]];

    return dataM.copy;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異热幔,居然都是意外死亡,警方通過查閱死者的電腦和手機讼庇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門绎巨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人巫俺,你說我怎么就攤上這事认烁。” “怎么了介汹?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵却嗡,是天一觀的道長。 經(jīng)常有香客問我嘹承,道長窗价,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任叹卷,我火速辦了婚禮撼港,結(jié)果婚禮上坪它,老公的妹妹穿的比我還像新娘。我一直安慰自己帝牡,他們只是感情好往毡,可當(dāng)我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著靶溜,像睡著了一般开瞭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上罩息,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天嗤详,我揣著相機與錄音,去河邊找鬼瓷炮。 笑死葱色,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的娘香。 我是一名探鬼主播苍狰,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼茅主!你這毒婦竟也來了舞痰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤诀姚,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后玷禽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赫段,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年矢赁,在試婚紗的時候發(fā)現(xiàn)自己被綠了糯笙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡撩银,死狀恐怖给涕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情额获,我是刑警寧澤够庙,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站抄邀,受9級特大地震影響耘眨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜境肾,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一剔难、第九天 我趴在偏房一處隱蔽的房頂上張望胆屿。 院中可真熱鬧,春花似錦偶宫、人聲如沸非迹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽憎兽。三九已至,卻和暖如春结闸,著一層夾襖步出監(jiān)牢的瞬間唇兑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工桦锄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留扎附,地道東北人。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓结耀,卻偏偏與公主長得像留夜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子图甜,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容