前端實現瀏覽器端大文件分片上傳

總結一下大文件分片上傳和斷點續傳的問題。由於文件過大(好比1G以上),必需要考慮上傳過程網絡中斷的狀況。http的網絡請求中自己就已經具有了分片上傳功能,當傳輸的文件比較大時,http協議自動會將文件切片(分塊),但這不是咱們如今說的重點,咱們要作的事是保證在網絡中斷後1G的文件已上傳的那部分在下次網絡鏈接時沒必要再重傳。因此咱們本地在上傳的時候,要將大文件進行分片,好比分紅1024*1024B,即將大文件分紅1M的片進行上傳,服務器在接收後,再將這些片合併成原始文件,這就是分片的基本原理。斷點續傳要求本地要記錄每一片的上傳的狀態,我經過三個狀態進行了標記(wait  loading  finish),當網絡中斷,再次鏈接後,從斷點處進行上傳。服務器經過文件名、總片數判斷該文件是否已所有上傳完成。服務器

        下面來講細節:網絡

  一、首先獲取文件(音視頻、圖片)多線程

分兩種狀況,一種是在相冊庫裏直接獲取,一種是調用相機。若是是經過UIImagePickerView來獲取(細節不詳述,網上一大堆),咱們會發現當你選定一個視頻的時候,會出現圖1的壓縮頁面,最後咱們的app獲取的視頻就是這個通過壓縮後的視頻(不是視頻庫裏的原始視頻,這裏有個注意點,操做完該壓縮視頻後記得釋放,系統不會幫你釋放的,須要你手動來操做,下面會說到),而後經過UIImagePickerView的協議方法中的- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info獲取視頻的Info併發

fileInfo = {app

    UIImagePickerControllerMediaType = "public.movie";async

    UIImagePickerControllerMediaURL = "file:///private/var/mobile/Containers/Data/Application/2AAE9E44-0E6D-4499-9AC3-93D44D8342EA/tmp/trim.F36EC46C-4219-43C8-96A7-FA7141AB64D2.MOV";ide

    UIImagePickerControllerReferenceURL = "assets-library://asset/asset.MOV?id=DEDA9406-3223-4F87-ABB2-98FB5F5EB9C4&ext=MOV";wordpress

}atom

UIImagePickerControllerMediaType是選取文件的類型,如KUTTypeImage,KUTTypeMovie。這裏注意一下movie和video的區別,一個是有聲音的視頻文件,一個是沒有聲音的視頻文件,固然還有Audio是隻有聲音沒有視頻。UIImagePickerControllerMediaURL是視頻的URL(若是是相機拍攝的,那麼這個就是原始拍攝獲得的視頻;若是是在相冊庫裏選擇的,那就是壓縮以後生成的視頻),注意這個URL不指向相冊庫,經過這個URL你能夠操做這個視頻如刪除,拷貝等,能夠獲取壓縮後的視頻的大小。UIImagePickerControllerReferenceURL是一個指向相冊的URL,官方的解釋是an NSURL that references an asset in the AssetsLibrary framework,經過這個URL,你能夠獲取視頻的全部信息,包括文件名,縮略圖,時長等(經過ALAssetsLibrary裏的assetsLibraryassetForURL:referenceURLresultBlock:)。spa

若是是相機拍攝的,注意兩個保存方法:圖片保存到相冊assetsLibrarywriteImageDataToSavedPhotosAlbum:UIImageJPEGRepresentation([infovalueForKey:UIImagePickerControllerOriginalImage],(CGFloat)1.0)metadata:nilcompletionBlock: failureBlock:

高保真壓縮圖片的方法NSData * UIImageJPEGRepresentation ( UIImage *image, CGFloat compressionQuality)

視頻保存到相冊:assetsLibrary writeVideoAtPathToSavedPhotosAlbum:MediaURL completionBlock:failureBlock:

 

到這裏,咱們就獲取了全部須要的文件以及文件信息。下面要作的就是將文件分片。

 

二、將獲取到的文件分片

首先,我將獲取到的文件保存在這這樣一個類中

@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;//總片數

@property (nonatomic,copy)NSString* fileInfo;

@property (nonatomic,strong)UIImage* fileImage;//文件縮略圖

@property (nonatomic,strong) NSMutableArray* fileArr;//標記每片的上傳狀態

@end

這樣咱們就能夠對每個CNFile對象進行操做了。

 

-(void)readDataWithChunk:(NSInteger)chunk file:(CNFile*)file{

  總片數的獲取方法:

    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);

    將文件分片,讀取每一片的數據:

    NSData* data;

    NSFileHandle *readHandle = [NSFileHandle fileHandleForReadingAtPath:file.filePath];

    [readHandle seekToFileOffset:offset * chunk];

    data = [readHandle readDataOfLength:offset];

}

 

這樣咱們就獲取了每一片要上傳的數據,而後詢問服務器,該片是否已經存在

(方法-(void)ifHaveData:(NSData*)data WithChunk:(NSInteger)chunk file:(CNFile*)file)

,若是存在,令chunk+1,重複上面的方法讀取下一片,直到服務器不存在該片,那麼上傳該片數據。在這個方法中注意設置該chunk的上傳狀態(wait  loading finish),這將關係到本地判斷該文件是否已所有上傳完成。

 

下一步就是上傳的過程:

-(void)uploadData:(NSData*) data WithChunk:(NSInteger) chunk file:(CNFile*)file;

在服務器返回該片上傳成功後,咱們要作的事有不少:

1)先將已經成功上傳的本片的flagfinish

[file.fileArr replaceObjectAtIndex:chunk withObject:@「finish"];

 

2)查看是否全部片的flag都已經置finish,若是都已經finishi,說明該文件上傳完成,那麼刪除該文件,上傳下一個文件或者結束。

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片的狀態",chunk+1);

 for(NSInteger i = chunk+1;i < chunks;i++)

  {

     NSString* flag = [file.fileArrobjectAtIndex:i];

      if ([flagisEqualToString:@"wait"]) {

             [me readDataWithChunk:ifileName:fileNamefile:file];

               break;

          }

   }

在第二、3步之間能夠有一個 2.5)判斷是否暫停上傳

if(me.isPause ==YES)

  {

  //將目前讀到了第幾個文件的第幾片保存到本地

     [self saveProgressWithChunk:chunk file:file];

      return ;

   }

這個操做實際上和上傳過程當中斷網是同樣的,爲了斷點續傳,在斷網或者暫停的時候,咱們要將目前的進度保存起來,以便下次上傳時略過前面已置finish的片。

而後還有一個問題,若是咱們就這樣線性的一片一片上傳,實際上失去了分片上傳的意義,應該結合多線程,使分片上傳過程併發執行,同時上傳多片,這樣就提升了上傳效率,並充分利用了網絡帶寬。

    dispatch_async(dispatch_queue_t queue, ^{

        [me readDataWithChunk: chunk];

    })

最後注意一下,每上傳完一個視頻,去設置裏看看你的app佔用的存儲空間有沒有增大哦,若是你沒有處理那個生成的壓縮視頻,你會發現你的app的空間佔用量是很大的。

詳細參考這篇文章:http://blog.ncmem.com/wordpress/2019/08/12/%e5%a4%a7%e6%96%87%e4%bb%b6%e6%96%ad%e7%82%b9%e7%bb%ad%e4%bc%a0-2/ 歡迎入羣一塊兒討論:374992201

相關文章
相關標籤/搜索