轉(zhuǎn)自:http://blog.csdn.NET/nndasdfg/article/details/51436731
總結(jié)一下大文件分片上傳和斷點續(xù)傳的問題。因為文件過大(比如1G以上),必須要考慮上傳過程網(wǎng)絡(luò)中斷的情況。http的網(wǎng)絡(luò)請求中本身就已經(jīng)具備了分片上傳功能鄙煤,當傳輸?shù)奈募容^大時癌幕,http協(xié)議自動會將文件切片(分塊),但這不是我們現(xiàn)在說的重點吩愧,我們要做的事是保證在網(wǎng)絡(luò)中斷后1G的文件已上傳的那部分在下次網(wǎng)絡(luò)連接時不必再重傳顾彰。所以我們本地在上傳的時候极阅,要將大文件進行分片,比如分成1024*1024B涨享,即將大文件分成1M的片進行上傳筋搏,服務器在接收后,再將這些片合并成原始文件厕隧,這就是分片的基本原理奔脐。斷點續(xù)傳要求本地要記錄每一片的上傳的狀態(tài),我通過三個狀態(tài)進行了標記(wait loading finish)栏账,當網(wǎng)絡(luò)中斷帖族,再次連接后栈源,從斷點處進行上傳挡爵。服務器通過文件名、總片數(shù)判斷該文件是否已全部上傳完成甚垦。
下面來說細節(jié):
1茶鹃、首先獲取文件(音視頻、圖片)
分兩種情況艰亮,一種是在相冊庫里直接獲取闭翩,一種是調(diào)用相機。如果是通過UIImagePickerView來獲绕!(細節(jié)不詳述疗韵,網(wǎng)上一大堆),我們會發(fā)現(xiàn)當你選定一個視頻的時候侄非,會出現(xiàn)圖1的壓縮頁面蕉汪,最后我們的app獲取的視頻就是這個經(jīng)過壓縮后的視頻(不是視頻庫里的原始視頻流译,這里有個注意點,操作完該壓縮視頻后記得釋放者疤,系統(tǒng)不會幫你釋放的福澡,需要你手動來操作,下面會說到)驹马,然后通過UIImagePickerView的協(xié)議方法中的
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
獲取視頻的Info
fileInfo = {
UIImagePickerControllerMediaType = "public.movie";
UIImagePickerControllerMediaURL = "file:///private/var/mobile/Containers/Data/Application/2AAE9E44-0E6D-4499-9AC3-93D44D8342EA/tmp/trim.F36EC46C-4219-43C8-96A7-FA7141AB64D2.MOV";
UIImagePickerControllerReferenceURL = "assets-library://asset/asset.MOV?id=DEDA9406-3223-4F87-ABB2-98FB5F5EB9C4&ext=MOV";
}
UIImagePickerControllerMediaType是選取文件的類型革砸,如KUTTypeImage,KUTTypeMovie糯累。這里注意一下movie和video的區(qū)別算利,一個是有聲音的視頻文件,一個是沒有聲音的視頻文件泳姐,當然還有Audio是只有聲音沒有視頻笔时。UIImagePickerControllerMediaURL是視頻的URL(如果是相機拍攝的,那么這個就是原始拍攝得到的視頻仗岸;如果是在相冊庫里選擇的允耿,那就是壓縮之后生成的視頻),注意這個URL不指向相冊庫扒怖,通過這個URL你可以操作這個視頻如刪除较锡,拷貝等,可以獲取壓縮后的視頻的大小盗痒。UIImagePickerControllerReferenceURL是一個指向相冊的URL蚂蕴,官方的解釋是an NSURL that references an asset in the AssetsLibrary framework,通過這個URL俯邓,你可以獲取視頻的所有信息骡楼,包括文件名,縮略圖稽鞭,時長等(通過ALAssetsLibraryassetsLibraryassetForURL:referenceURLresultBlock:)鸟整。
如果是相機拍攝的,注意兩個保存方法:圖片保存到相冊:
assetsLibrarywriteImageDataToSavedPhotosAlbum:UIImageJPEGRepresentation([infovalueForKey:UIImagePickerControllerOriginalImage],(CGFloat)1.0)metadata:nilcompletionBlock: failureBlock:
高保真壓縮圖片的方法:
NSData * UIImageJPEGRepresentation ( UIImage *image, CGFloat compressionQuality)
視頻保存到相冊:
assetsLibrary writeVideoAtPathToSavedPhotosAlbum:MediaURL completionBlock:failureBlock:
到這里朦蕴,我們就獲取了所有需要的文件以及文件信息篮条。下面要做的就是將文件分片。
2吩抓、將獲取到的文件分片
首先涉茧,我將獲取到的文件保存在這這樣一個類中
@interface CNFile : NSObject
@property (nonatomic,copy)NSString* fileType;//image or movie
@property (nonatomic,copy)NSString* filePath;//文件在app中路徑
@property (nonatomic,copy)NSString* fileName;//文件名
@property (nonatomic,assign)NSInteger fileSize;//文件大小
@property (nonatomic,assign) NSInteger trunks;//總片數(shù)
@property (nonatomic,copy)NSString* fileInfo;
@property (nonatomic,strong)UIImage* fileImage;//文件縮略圖
@property (nonatomic,strong) NSMutableArray* fileArr;//標記每片的上傳狀態(tài)
@end
這樣我們就可以對每一個CNFile對象進行操作了。
-(void)readDataWithChunk:(NSInteger)chunk file:(CNFile*)file{
總片數(shù)的獲取方法:
int offset =1024*1024;(每一片的大小是1M)
NSInteger chunks = (file.fileSize%1024==0)?((int)(file.fileSize/1024*1024)):((int)(file.fileSize/(1024*1024) + 1));
NSLog(@"chunks = %ld",(long)chunks);
將文件分片疹娶,讀取每一片的數(shù)據(jù):
NSData* data;
NSFileHandle *readHandle = [NSFileHandle fileHandleForReadingAtPath:file.filePath];
[readHandle seekToFileOffset:offset * chunk];
data = [readHandle readDataOfLength:offset];
}
這樣我們就獲取了每一片要上傳的數(shù)據(jù)伴栓,然后詢問服務器,該片是否已經(jīng)存在
(方法-(void)ifHaveData:(NSData*)data WithChunk:(NSInteger)chunk file:(CNFile*)file)
,如果存在钳垮,令chunk+1除师,重復上面的方法讀取下一片,直到服務器不存在該片扔枫,那么上傳該片數(shù)據(jù)汛聚。在這個方法中注意設(shè)置該chunk的上傳狀態(tài)(wait loading finish),這將關(guān)系到本地判斷該文件是否已全部上傳完成短荐。
下一步就是上傳的過程:
-(void)uploadData:(NSData*) data WithChunk:(NSInteger) chunk file:(CNFile*)file倚舀;
在服務器返回該片上傳成功后,我們要做的事有很多:
1)先將已經(jīng)成功上傳的本片的flag置finish
[file.fileArr replaceObjectAtIndex:chunk withObject:@“finish"];
2)查看是否所有片的flag都已經(jīng)置finish忍宋,如果都已經(jīng)finish痕貌,說明該文件上傳完成,那么刪除該文件糠排,上傳下一個文件或者結(jié)束舵稠。
for (NSInteger j =0; j<chunks; j++){
if (j == chunks || ((j == chunks -1)&&([file.fileArr[j] isEqualToString:@"finish"])))
[me deleteFile:file.filePath];
[me readNextFile];
}
3)如果沒有都finish,那么看本地下一chunk對應的flag是否是wait
NSLog(@"查看第%ld片的狀態(tài)",chunk+1);
for(NSInteger i = chunk+1;i < chunks;i++)
{
NSString* flag = [file.fileArrobjectAtIndex:i];
if ([flagisEqualToString:@"wait"]) {
[me readDataWithChunk:i fileName:fileName file:file];
break;
}
}
在第2入宦、3步之間可以有一個 2.5)判斷是否暫停上傳
if(me.isPause ==YES)
{
//將目前讀到了第幾個文件的第幾片保存到本地
[self saveProgressWithChunk:chunk file:file];
return ;
}
這個操作實際上和上傳過程中斷網(wǎng)是一樣的哺徊,為了斷點續(xù)傳,在斷網(wǎng)或者暫停的時候乾闰,我們要將目前的進度保存起來落追,以便下次上傳時略過前面已置finish的片。
然后還有一個問題涯肩,如果我們就這樣線性的一片一片上傳轿钠,實際上失去了分片上傳的意義,應該結(jié)合多線程病苗,使分片上傳過程并發(fā)執(zhí)行疗垛,同時上傳多片,這樣就提高了上傳效率硫朦,并充分利用了網(wǎng)絡(luò)帶寬贷腕。
dispatch_async(dispatch_queue_t queue, ^{
[me readDataWithChunk: chunk];
})
最后注意一下,每上傳完一個視頻阵幸,去設(shè)置里看看你的app占用的存儲空間有沒有增大哦花履,如果你沒有處理那個生成的壓縮視頻,你會發(fā)現(xiàn)你的app的空間占用量是很大的挚赊。