iOS里實(shí)現(xiàn)multipart/form-data格式上傳文件

1.使用微博開發(fā)的一個(gè)“發(fā)送帶圖片微博”的接口來測(cè)試,這是接口地址驾凶,這里面明確說明需要使用multipart/form-data格式提交圖片牙甫。關(guān)于使用微博開放平臺(tái)api、授權(quán)之類的就不說了调违。

2.multipart/from-data是一種進(jìn)行表單提交時(shí)的消息格式窟哺。表單提交數(shù)據(jù)的時(shí)候,默認(rèn)類型是application/x-www-form-urlencoded技肩,也就是key=value的鍵值對(duì)格式且轨,提交文件的時(shí)候使用multipart/from-data。因?yàn)槭潜韱翁峤恍樾觯詇ttp請(qǐng)求方式是POST旋奢。然后在請(qǐng)求頭里設(shè)置Content-Type為multipart/from-data指定請(qǐng)求的格式。

  NSURL *URL = [[NSURL alloc]initWithString:urlString];
      request = [[NSMutableURLRequest alloc]initWithURL:URL cachePolicy:(NSURLRequestUseProtocolCachePolicy) timeoutInterval:30];
  request.HTTPMethod = @"POST";

    NSString *boundary = @"wfWiEWrgEFA9A78512weF7106A";
    request.allHTTPHeaderFields = @{
                                    @"Content-Type":[NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary]
                                    };

注意到在Content-Type里還有個(gè)boundary,顧名思義雳锋,這個(gè)東西是用來做分隔的字符串黄绩。boundary本身沒有特殊要求,只要不會(huì)和其他內(nèi)容混淆就好玷过,所以盡量復(fù)雜些爽丹。

3.POST請(qǐng)求,參數(shù)都放在請(qǐng)求體里面辛蚊,而請(qǐng)求體是這里的關(guān)鍵粤蝎,multipart/form-data就是一種格式,約定請(qǐng)求體的數(shù)據(jù)如何存放袋马。放個(gè)例子先:

--wfWiEWrgEFA9A78512weF7106A                    //部分1
Content-Disposition: form-data; name="status"   //部分2

哈哈哈                                           //部分3
--wfWiEWrgEFA9A78512weF7106A
Content-Disposition: form-data; name="source"

2582981980
--wfWiEWrgEFA9A78512weF7106A
Content-Disposition: form-data; name="access_token"

2.00nVEJoBgbvnoCc54e19c4c4NksmWC
--wfWiEWrgEFA9A78512weF7106A
Content-Disposition: form-data; name="pic"; filename="卡車.png"
Content-Type=image/png

...這里是文件的二進(jìn)制數(shù)據(jù)...                      //部分4
--wfWiEWrgEFA9A78512weF7106A--                //部分5

上面的“//部分X”是注釋哈初澎。
(1)部分1是“--”+boundary,即雙減號(hào)加分隔符虑凛,然后換行碑宴,注意換行是使用“\r\n”,因?yàn)檫@些標(biāo)準(zhǔn)開始都是在html中使用的桑谍。測(cè)試了微博接口延柠,換行也不能出錯(cuò)。
(2)部分2的格式是:Content-Disposition: form-data; name="xxx"锣披,這里的xxx是接口的參數(shù)臊诊,比如微博測(cè)試接口有一個(gè)參數(shù)是“status”,那status寫在這里今魔。然后是兩個(gè)換行,即“\r\n\r\n”整以。
(3)有key就有value,上面說了key的位置,這里就是value的位置峻仇」冢“status”字段代表的是微博的正文內(nèi)容,所以就把微博正文內(nèi)容放在部分3位置摄咆,即“哈哈哈”帆调。然后換行。
(4)然后就是部分1豆同、2、3這個(gè)結(jié)構(gòu)重復(fù)含鳞,每一個(gè)重復(fù)結(jié)構(gòu)對(duì)應(yīng)著接口里的一個(gè)字段的數(shù)據(jù)影锈。直到你要上傳的文件,部分4蝉绷。部分4這一節(jié)多出了“ilename="卡車.png";Content-Type=image/png”這些內(nèi)容鸭廷,其實(shí)這里可以還有其他的內(nèi)容可以設(shè)置,charset和content-transfer-encoding熔吗,都是用于描述這一部分?jǐn)?shù)據(jù)辆床。具體參考rfc標(biāo)準(zhǔn)。需要注意的是桅狠,name\filename是帶引號(hào)的讼载,而Content-Type是沒有的,就這一個(gè)細(xì)節(jié)中跌,廢掉了我一下午白傻獭!
這里的Content-Type值是這里要上傳文件的格式漩符,也不能錯(cuò)一喘。
(5)部分4這里是需要上傳的文件的二進(jìn)制數(shù)據(jù),當(dāng)然其他部分也是同樣是要轉(zhuǎn)成NSData的嗜暴。
(6)最后部分5是結(jié)束標(biāo)識(shí)凸克,--wfWiEWrgEFA9A78512weF7106A這部分是和前面的分割符一樣,但接下來不是換行闷沥,而是繼續(xù)“--”萎战,整個(gè)請(qǐng)求體結(jié)束。這也是個(gè)坑啊狐赡,之前以為沒有“--”撞鹉!

具體代碼如下:

NSMutableData *postData = [[NSMutableData alloc]init];//請(qǐng)求體數(shù)據(jù)
    for (NSString *key in params) {
        //循環(huán)參數(shù)按照部分1疟丙、2、3那樣循環(huán)構(gòu)建每部分?jǐn)?shù)據(jù)
        NSString *pair = [NSString stringWithFormat:@"--%@\r\nContent-Disposition: form-data; name=\"%@\"\r\n\r\n",boundary,key];
        [postData appendData:[pair dataUsingEncoding:NSUTF8StringEncoding]];
        
        id value = [params objectForKey:key];
        if ([value isKindOfClass:[NSString class]]) {
            [postData appendData:[value dataUsingEncoding:NSUTF8StringEncoding]];
        }else if ([value isKindOfClass:[NSData class]]){
            [postData appendData:value];
        }
        [postData appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    }
    
    //文件部分
    NSString *filename = [filePath lastPathComponent];
    NSString *contentType = AFContentTypeForPathExtension([filePath pathExtension]);
    
    NSString *filePair = [NSString stringWithFormat:@"--%@\r\nContent-Disposition: form-data; name=\"%@\"; filename=\"%@\"
Content-Type=%@\r\n\r\n",boundary,fileKey,filename,contentType];
    [postData appendData:[filePair dataUsingEncoding:NSUTF8StringEncoding]];
    [postData appendData:fileData]; //加入文件的數(shù)據(jù)
[postData appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; 

如果有多個(gè)文件鸟雏,就重復(fù)設(shè)置文件部分享郊,使用不同的`name`標(biāo)識(shí)。
    
    //設(shè)置結(jié)尾
    [postData appendData:[[NSString stringWithFormat:@"--%@--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
        request.HTTPBody = postData;
    //設(shè)置請(qǐng)求頭總數(shù)據(jù)長(zhǎng)度
    [request setValue:[NSString stringWithFormat:@"%lu",(unsigned long)postData.length] forHTTPHeaderField:@"Content-Length"];

然后根據(jù)文件后綴名可以獲取對(duì)應(yīng)的Content-Type孝鹊,來源AFNetWorking:

static inline NSString * AFContentTypeForPathExtension(NSString *extension) {
#ifdef __UTTYPE__
    NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL);
    NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType);
    if (!contentType) {
        return @"application/octet-stream";
    } else {
        return contentType;
    }
#else
#pragma unused (extension)
    return @"application/octet-stream";
#endif
}

需要注意的:
1炊琉、每個(gè)該換行的地方都不能少,也不能多又活,且換行為“\r\n”
2苔咪、Content-Type的值沒有引號(hào)
3、整個(gè)請(qǐng)求體是以“--”結(jié)束的柳骄。

然后因?yàn)閜ostData里面混進(jìn)了圖片的二進(jìn)制數(shù)據(jù)团赏,沒法從NSData轉(zhuǎn)成字符串,在調(diào)試的時(shí)候沒法直觀的查看哪里出了問題耐薯。我的做法是把添加文件數(shù)據(jù)舔清,改成一個(gè)字符串的數(shù)據(jù),比如:

[postData appendData:[@"測(cè)試文件數(shù)據(jù)" dataUsingEncoding:NSUTF8StringEncoding]];
//[postData appendData:fileData]; //加入文件的數(shù)據(jù)

最后是項(xiàng)目代碼:github地址曲初。微博授權(quán)功能已經(jīng)在里面了体谒,先授權(quán),在測(cè)試提交圖片臼婆。上傳后看控制臺(tái)輸入抒痒,成功后可以在授權(quán)的微博賬號(hào)里看見剛發(fā)的這條微博。


這個(gè)很久沒更新了颁褂,相關(guān)知識(shí)可以直接參考AFNetWorking的封裝故响,上傳文件部分就是這個(gè)原理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末颁独,一起剝皮案震驚了整個(gè)濱河市被去,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌奖唯,老刑警劉巖惨缆,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異丰捷,居然都是意外死亡坯墨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門病往,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捣染,“玉大人,你說我怎么就攤上這事停巷∷H粒” “怎么了榕栏?”我有些...
    開封第一講書人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蕾各。 經(jīng)常有香客問我扒磁,道長(zhǎng),這世上最難降的妖魔是什么式曲? 我笑而不...
    開封第一講書人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任妨托,我火速辦了婚禮,結(jié)果婚禮上吝羞,老公的妹妹穿的比我還像新娘兰伤。我一直安慰自己,他們只是感情好钧排,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開白布敦腔。 她就那樣靜靜地躺著,像睡著了一般恨溜。 火紅的嫁衣襯著肌膚如雪会烙。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,521評(píng)論 1 304
  • 那天筒捺,我揣著相機(jī)與錄音,去河邊找鬼纸厉。 笑死系吭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的颗品。 我是一名探鬼主播肯尺,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼躯枢!你這毒婦竟也來了则吟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤锄蹂,失蹤者是張志新(化名)和其女友劉穎氓仲,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體得糜,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡敬扛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了朝抖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片啥箭。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖治宣,靈堂內(nèi)的尸體忽然破棺而出急侥,到底是詐尸還是另有隱情砌滞,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布坏怪,位于F島的核電站贝润,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏陕悬。R本人自食惡果不足惜题暖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望捉超。 院中可真熱鬧胧卤,春花似錦、人聲如沸拼岳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)惜纸。三九已至叶撒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間耐版,已是汗流浹背祠够。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留粪牲,地道東北人古瓤。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像腺阳,于是被迫代替她去往敵國(guó)和親落君。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理亭引,服務(wù)發(fā)現(xiàn)绎速,斷路器,智...
    卡卡羅2017閱讀 134,657評(píng)論 18 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,133評(píng)論 25 707
  • 1.四種常見的 POST 提交數(shù)據(jù)方式對(duì)應(yīng)的content-type取值 1.1application/x-www...
    Iris_Fighting閱讀 22,052評(píng)論 2 17
  • iOS開發(fā)系列--網(wǎng)絡(luò)開發(fā) 概覽 大部分應(yīng)用程序都或多或少會(huì)牽扯到網(wǎng)絡(luò)開發(fā)焙蚓,例如說新浪微博纹冤、微信等,這些應(yīng)用本身可...
    lichengjin閱讀 3,661評(píng)論 2 7
  • 「主 題」:詢問朋友想要什么生日禮物 「理性目的」:明確送什么生日禮物 「體驗(yàn)?zāi)康摹梗鹤屗谏者@天感到開心...
    棠七七閱讀 201評(píng)論 0 0