*網(wǎng)上看了很多關(guān)于斷點(diǎn)續(xù)傳的文章王凑,但是發(fā)現(xiàn)各個(gè)項(xiàng)目的需求可能有所差異杂穷,最近做了一個(gè)關(guān)于上傳視頻的,這里分享一下我的處理方式女责,處理不好的地方請(qǐng)諒解持灰。*
這里我先說(shuō)一下我們處理的邏輯:由于上傳一個(gè)較大的視頻文件失敗的幾率較高絮缅,所以打算把每個(gè)視頻文件分割成多個(gè)小的塊,然后一塊一塊的上傳撩鹿,這樣就提高的上傳的成功率谦炬,而且上傳的部分會(huì)被保存到服務(wù)器,中途失敗节沦,再繼續(xù)上傳就會(huì)接著上次未完成的上傳键思。這個(gè)功能需要后臺(tái)同事的配合,制定一套屬于自己的邏輯甫贯。上傳分為兩部分部分:查詢 和 上傳吼鳞。
-查詢
1.加密字符串(具體后臺(tái)可以定義):首先和后端約定了需要傳的參數(shù)(此處我用的是“md5value”)。每次上傳之前先調(diào)用一次查詢接口叫搁,然后保留md5value的值存到本地赔桌,下次調(diào)用查詢接口的時(shí)候獲取到的md5value與本地相對(duì)比,如果是一樣的就保留本地值渴逻,不同就替換疾党。此外接口還會(huì)返回當(dāng)前上傳到第幾塊,和每塊定義的大胁肄取(此處定義為separatedSize)雪位。
2.接下來(lái)是計(jì)算需要把視頻文件分為多少塊上傳,首先需要通過(guò)本地路徑獲取視頻data梨撞,用data的長(zhǎng)度除以每塊的大斜⑾础(separatedSize),得到塊數(shù)卧波;
//視頻總大小
NSData *video_total = [NSData dataWithContentsOfURL:filePath];
//視頻總塊數(shù)
int video_num = ceil(video_total.length/[separatedSize intValue]);
3.確定了塊數(shù)时肿,這里就需要分割文件,直接用NSFileHandle就可以港粱。這里我用到了for循環(huán)螃成,seekToFileOffset方法,移動(dòng)文件指針到目標(biāo)位置(比如第2塊:** [handle seekToFileOffset:[separatedSize intValue] * (2-1)];)“i”是for循環(huán)查坪,默認(rèn)從1開(kāi)始寸宏,查詢接口返回當(dāng)前傳到第幾塊,如果有值咪惠,i就從接口返回的值開(kāi)始循環(huán)。因?yàn)橹岸际蔷执笮×艿恚?dāng)傳到最后一塊遥昧,無(wú)法確定大小覆醇,所以直接用readDataToEndOfFile**
NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:filePath.path];
// 移動(dòng)文件指針
[handle seekToFileOffset:[separatedSize intValue] * (i-1)];
//讀取數(shù)據(jù)指定大小的數(shù)據(jù)
NSData *blockData = nil;
if (i ==“最后一塊”) {
blockData = [handle readDataToEndOfFile];
}else{
blockData = [handle readDataOfLength:[separatedSize intValue]];
}
4.接下來(lái)就是上傳了,直接用for循環(huán)調(diào)用AFNetWorking的上傳方法就可以了
[manager POST:@"" parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
[formData appendPartWithFileData:fileData name:@"mfile" fileName:@"ios.mp4" mimeType:@"video/quicktime"];
} progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
注意的是需要確定每塊上傳成功后再執(zhí)行for循環(huán)胰坟,如果中途失敗了直接return箕憾,否則和后臺(tái)的對(duì)接會(huì)出錯(cuò)神妹。
這里我是創(chuàng)建了一個(gè)串行隊(duì)列
// 1.創(chuàng)建一個(gè)串行隊(duì)列,保證for循環(huán)依次執(zhí)行
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
// 3.創(chuàng)建一個(gè)數(shù)目為1的信號(hào)量常摧,用于“卡”for循環(huán),等上次循環(huán)結(jié)束在執(zhí)行下一次的for循環(huán)
dispatch_semaphore_t sema = dispatch_semaphore_create(1);
for (int i = chunkValue; i <= video_num; i++) {
// 開(kāi)始執(zhí)行for循環(huán)威创,讓信號(hào)量-1落午,這樣下次操作須等信號(hào)量>=0才會(huì)繼續(xù),否則下次操作將永久停止
[self requestUploadVideo_new:item dic:[dict mj_JSONString] fileData:blockData name:item.fileName fileName:item.fileName progress:^(NSProgress *progress) {
} success:^(id object) {
//block成功再執(zhí)行
dispatch_semaphore_signal(sema);