上传/下载文件过大(如>1G)时,要考虑到网络中断的情况。目前HTTP协议的网络请求本身就已经具备了分片上传能力,传输较大文件比时,协议会自动将文件切片(分块),这种切片是协议内切片方式。在处理大文件时,还可以将大文件主动切片(具体数量自定,分块大小建议相等,并编号),服务器在接收后,再将这些切片按序合并,这就是分片的基本原理。断点续传要求本地要记录每一片的上传状态(如wait、loading、finish),当网络中断后再次连接时,从断点处进行上传。服务器通过文件名、总片数判断该文件是否已全部上传完成。
1. iOS获取文件(音视频、图片)
分两种情况,一种是在相册库里直接获取,一种是调用相机。如果是通过UIImagePickerView来获取,我们会发现当你选定一个视频的时候,会出现图1的压缩页面,最后我们的app获取的视频就是这个经过压缩后的视频(不是视频库里的原始视频,这里有个注意点,操作完该压缩视频后记得释放,系统不会帮你释放的,需要你手动来操作,下面会说到)。
// 然后通过UIImagePickerView的协议方法中的
- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)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的区别,一个是有声音的视频文件,一个是没有声音的视频文件,当然还有Audio是只有声音没有视频)。
UIImagePickerControllerMediaURL是视频的URL(如果是相机拍摄的,那么这个就是原始拍摄得到的视频;如果是在相册库里选择的,那就是压缩之后生成的视频),这个URL不指向相册库,通过这个URL你可以操作这个视频如删除,拷贝等,可以获取压缩后的视频的大小。
UIImagePickerControllerReferenceURL是一个指向相册的URL(官方释义,an NSURL that references an asset in the AssetsLibrary framework),通过这个URL,你可以获取视频的所有信息,包括文件名,缩略图,时长等(通过ALAssetsLibrary里的assetsLibraryassetForURL:referenceURLresultBlock:)。
如果是相机拍摄的,注意两个保存方法:
// 高保真压缩图片的方法
NSData *imageData = UIImageJPEGRepresentation(UIImage *image, CGFloat compressionQuality);
// 图片保存到相册
ALAssetsLibrary *assetsLib = [[ALAssetsLibrary alloc] init];
[assetsLib writeImageDataToSavedPhotosAlbum:imageData metadata:nil completionBlock:nil];
// 视频保存到相册:
[assetsLib writeVideoAtPathToSavedPhotosAlbum:MediaURL completionBlock:nil failureBlock:nil];
到这里,我们就获取了所有需要的文件以及文件信息。
2. 将文件切片
由于我们在处理被分片的文件时,涉及用到文件切片的类型、路径、名称、总篇数等信息,因此,我将创建一个文件切片的Model类:
@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)NSIntegerfileSize;//文件大小
@property (nonatomic,assign)NSIntegertrunks;//总片数
@property(nonatomic,copy)NSString* fileInfo;
@property(nonatomic,strong)UIImage* fileImage;//文件缩略图
@property(nonatomic,strong) NSMutableArray* fileArr;//标记每片的上传状态
@end
这样我们就可以对每一个CNFile对象进行操作了。
-(NSData *)readDataWithChunk:(NSInteger)chunk file:(CNFile*)file{
//每一片的大小是1M
int perSize = 1024*1024;
//总片数的获取方法:
NSInteger chunks = (file.fileSize%1024 == 0)?((int)(file.fileSize/perSize)):((int)(file.fileSize/perSize + 1));
NSLog(@"chunks = %ld",(long)chunks);
// 将文件分片,读取每一片的数据:
NSData* data;
NSFileHandle*readHandle = [NSFileHandlefileHandleForReadingAtPath:file.filePath];
[readHandle seekToFileOffset:perSize * chunk];
data = [readHandlereadDataOfLength:offset];
return data;
}
3. 文件切片后的上传
上传前,依次询问服务器,该片是否已经存在。如果存在,令chunk+1,重复执行以下方法读取下一片,直到服务器不存在该片,那么上传该片数据。在这个方法中注意设置该chunk的上传状态(wait loading finish),这将关系到本地判断该文件是否已全部上传完成。
- (void)ifHaveData:(NSData*)data WithChunk:(NSInteger)chunk file:(CNFile*)file;
上传的过程:
-(void)uploadData:(NSData*) data WithChunk:(NSInteger) chunk file:(CNFile*)file;
在服务端返回该片上传成功后,我们要做的事有很多:
- 先将已经成功上传的本片的flag置finish
[file.fileArr replaceObjectAtIndex:chunk withObject:@"finish"];
- 查看是否所有片的flag都已为finish,若都已finish,说明该文件上传完成,那么删除该文件,上传下一个文件或者结束。
for(int j =0; j < chunks; j++) {
if(j == chunks || ((j == chunks -1)&&([file.fileArr[j]isEqualToString:@"finish"]))) {
[me deleteFile:file.filePath];
[me readNextFile];
}
{
- 如果没有都finish,那么看本地下一chunk对用的flag是否是wait
NSLog(@"查看第%ld片的状态",chunk+1);
for(NSInteger i = chunk+1; i < chunks; i++) {
NSString* flag = [file.fileArrobjectAtIndex:i];
if([flagisEqualToString:@"wait"]) {
[mereadDataWithChunk:ifileName:fileNamefile:file];
break;
}
}
在第2、3步之间可以有一个 “2.5” ,判断是否暂停上传:
if(me.isPause ==YES) {
// 将目前读到了第几个文件的第几片保存到本地(注意,对比URLSession中的ResumeData的意义!)
[self saveProgressWithChunk:chunk file:file];
return;
}
这个操作实际上和上传过程中断网是一样的,为了断点续传,在断网或者暂停的时候,我们要将目前的进度保存起来,以便下次上传时略过前面已置finish的部分。
显然,我们可以结合多线程使分片上传过程并发执行,同时上传多片,这样就提高了上传效率,并充分利用网络带宽:
dispatch_async(dispatch_queue_t queue, ^{
[me readDataWithChunk: chunk];
})
最后注意一下,每上传完一个视频,不要忘记释放临时生成的压缩视频,否则,app的空间占用量会剧增。
4. 示例
以下是在网上搜集整理的异步分块上传的实例代码,正确性未知,思路仅供参考:
#pragma mark - 异步上传
- (void)uploadObjectAsync:(NSString *)FileURL objectKey:(NSString *)objectKey{
OSSPutObjectRequest *put = [OSSPutObjectRequest new];
NSLog(@"objectKey is %@",objectKey);
// required fields
put.bucketName = bucketName;
put.objectKey = objectKey;
put.uploadingFileURL = [NSURL fileURLWithPath:FileURL];
// optional fields
put.uploadProgress = ^(int64_t bytesSent, int64_t totalByteSent, int64_t totalBytesExpectedToSend) {
NSLog(@"%lld, %lld, %lld", bytesSent, totalByteSent, totalBytesExpectedToSend);
dispatch_async(dispatch_get_main_queue(), ^{
float gress = (double)totalByteSent/totalBytesExpectedToSend;
if([self.dclickDelegate respondsToSelector:@selector(WaveProgressPlus:)]){
[self.dclickDelegate performSelector:@selector(WaveProgressPlus:) withObject:@(gress)];
NSLog(@"gress123上传中");
}
});
};
OSSTask * putTask = [client putObject:put];
[putTask continueWithBlock:^id(OSSTask *task) {
NSLog(@" putTask :%@",putTask.result);
NSLog(@"objectKey: %@", put.objectKey);
uploadputkey = put.objectKey;
if (!task.error) {
NSLog(@"upload object success!");
if([self.dclickDelegate respondsToSelector:@selector(dissbackView)]){
[self.dclickDelegate performSelector:@selector(dissbackView)];
}
[self signAccessObjectURL];
}
else {
NSLog(@"upload object failed, error: %@" , task.error);
}
return nil;
}];
}
#pragma mark - 同步上传
- (void)uploadObjectSync:(NSString *)FileURL objectKey:(NSString *)objectKey {
OSSPutObjectRequest * put = [OSSPutObjectRequest new];
// required fields
put.bucketName = bucketName;
put.objectKey = objectKey;
put.uploadingFileURL = [NSURL fileURLWithPath:FileURL];
// optional fields
put.uploadProgress = ^(int64_t bytesSent, int64_t totalByteSent, int64_t totalBytesExpectedToSend) {
NSLog(@"uploadProgress is %lld, %lld, %lld", bytesSent, totalByteSent, totalBytesExpectedToSend);
dispatch_async(dispatch_get_main_queue(), ^{
float gress = totalByteSent/totalBytesExpectedToSend;
if([self.dclickDelegate respondsToSelector:@selector(WaveProgressPlus:)]){
[self.dclickDelegate performSelector:@selector(WaveProgressPlus:) withObject:@(gress)];
NSLog(@"gress123");
}
});
};
OSSTask * putTask = [client putObject:put];
[putTask waitUntilFinished]; // 阻塞直到上传完成
NSLog(@" putTask :%@",putTask.result);
if (!putTask.error) {
NSLog(@"upload object success!");
NSLog(@" putTask :%@",putTask.result);
NSLog(@"objectKey: %@", put.objectKey);
uploadputkey = put.objectKey;
if([self.dclickDelegate respondsToSelector:@selector(dissbackView)]){
[self.dclickDelegate performSelector:@selector(dissbackView)];
}
[self signAccessObjectURL];
}
else {
NSLog(@"upload object failed, error: %@" , putTask.error);
}
}
#pragma mark - 追加上传
- (void)appendObject {
OSSAppendObjectRequest * append = [OSSAppendObjectRequest new];
// 必填字段
append.bucketName = @"android-test";
append.objectKey = @"file1m";
append.appendPosition = 0; // 指定从何处进行追加
NSString * docDir = [self getDocumentDirectory];
append.uploadingFileURL = [NSURL fileURLWithPath:[docDir stringByAppendingPathComponent:@"file1m"]];
// 可选字段
append.uploadProgress = ^(int64_t bytesSent, int64_t totalByteSent, int64_t totalBytesExpectedToSend) {
NSLog(@"%lld, %lld, %lld", bytesSent, totalByteSent, totalBytesExpectedToSend);
};
// append.contentType = @"";
// append.contentMd5 = @"";
// append.contentEncoding = @"";
// append.contentDisposition = @"";
OSSTask * appendTask = [client appendObject:append];
NSLog(@" putTask :%@",appendTask.result);
[appendTask continueWithBlock:^id(OSSTask *task) {
NSLog(@"objectKey: %@", append.objectKey);
if (!task.error) {
NSLog(@"append object success!");
OSSAppendObjectResult * result = task.result;
NSString * etag = result.eTag;
long nextPosition = result.xOssNextAppendPosition;
NSLog(@"etag: %@, nextPosition: %ld", etag, nextPosition);
}
else {
NSLog(@"append object failed, error: %@" , task.error);
}
return nil;
}];
}
#pragma mark - 异步下载
- (void)downloadObjectAsync {
OSSGetObjectRequest * request = [OSSGetObjectRequest new];
// required
request.bucketName = @"android-test";
request.objectKey = @"file1m";
//optional
request.downloadProgress = ^(int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {
NSLog(@"%lld, %lld, %lld", bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
};
OSSTask * getTask = [client getObject:request];
[getTask continueWithBlock:^id(OSSTask *task) {
if (!task.error) {
NSLog(@"download object success!");
OSSGetObjectResult * getResult = task.result;
NSLog(@"download dota length: %lu", [getResult.downloadedData length]);
}
else {
NSLog(@"download object failed, error: %@" ,task.error);
}
return nil;
}];
}
#pragma mark - 同步下载
- (void)downloadObjectSync {
OSSGetObjectRequest *request = [OSSGetObjectRequest new];
// required
request.bucketName = @"android-test";
request.objectKey = @"file1m";
//optional
request.downloadProgress = ^(int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {
NSLog(@"%lld, %lld, %lld", bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
};
// NSString * docDir = [self getDocumentDirectory];
// request.downloadToFileURL = [NSURL fileURLWithPath:[docDir stringByAppendingPathComponent:@"downloadfile"]];
OSSTask * getTask = [client getObject:request];
[getTask waitUntilFinished];
if (!getTask.error) {
OSSGetObjectResult * result = getTask.result;
NSLog(@"download data length: %lu", [result.downloadedData length]);
}
else {
NSLog(@"download data error: %@", getTask.error);
}
}
#pragma mark - 获取meta
- (void)headObject {
OSSHeadObjectRequest * head = [OSSHeadObjectRequest new];
head.bucketName = @"android-test";
head.objectKey = @"file1m";
OSSTask * headTask = [client headObject:head];
[headTask continueWithBlock:^id(OSSTask *task) {
if (!task.error) {
OSSHeadObjectResult * headResult = task.result;
NSLog(@"all response header: %@", headResult.httpResponseHeaderFields);
// some object properties include the 'x-oss-meta-*'s
NSLog(@"head object result: %@", headResult.objectMeta);
}
else {
NSLog(@"head object error: %@", task.error);
}
return nil;
}];
}
#pragma mark - 删除Object
- (void)deleteObject {
OSSDeleteObjectRequest * delete = [OSSDeleteObjectRequest new];
delete.bucketName = @"android-test";
delete.objectKey = @"file1m";
OSSTask * deleteTask = [client deleteObject:delete];
[deleteTask continueWithBlock:^id(OSSTask *task) {
if (!task.error) {
NSLog(@"delete success !");
}
else {
NSLog(@"delete erorr, error: %@", task.error); }
return nil;
}];
}
#pragma mark - 复制Object
- (void)copyObjectAsync {
OSSCopyObjectRequest *copy = [OSSCopyObjectRequest new];
copy.bucketName = @"android-test"; // 复制到哪个bucket
copy.objectKey = @"file_copy_to"; // 复制为哪个object
copy.sourceCopyFrom = [NSString stringWithFormat:@"/%@/%@", @"android-test", @"file1m"]; // 从哪里复制
OSSTask * copyTask = [client copyObject:copy];
[copyTask continueWithBlock:^id(OSSTask *task) {
if (!task.error) {
NSLog(@"copy success!");
}
else {
NSLog(@"copy error, error: %@", task.error);
}
return nil;
}];
}
#pragma mark - 签名URL授予第三方访问
- (void)signAccessObjectURL {
NSString * constrainURL = nil;
NSString * publicURL = nil;
// sign constrain url
OSSTask * task = [client presignConstrainURLWithBucketName:bucketName withObjectKey:uploadputkey withExpirationInterval:60 * 30];
// if (!task.error) {
// constrainURL = task.result;
// NSLog(@"constrainURL is %@",constrainURL);
// postPublicURL = publicURL;
// [self createTranscodeJobFlow];
// } else {
// NSLog(@"error: %@", task.error);
// }
// sign public url
task = [client presignPublicURLWithBucketName:bucketName withObjectKey:uploadputkey];
if (!task.error) {
publicURL = task.result;
NSLog(@"publicURL is %@",publicURL);
postPublicURL = publicURL;
[self createTranscodeJobFlow];
}
else {
NSLog(@"sign url error: %@", task.error);
}
}
#pragma mark 分块上传
- (void)multipartUpload:(NSString *)FileURL objectKey:(NSString *)objectKey{
__block NSString * uploadId = nil;
__block NSMutableArray * partInfos = [NSMutableArray new];
NSString * uploadToBucket = bucketName;
NSString * uploadObjectkey = objectKey;
OSSInitMultipartUploadRequest * init = [OSSInitMultipartUploadRequest new];
init.bucketName = uploadToBucket;
init.objectKey = uploadObjectkey;
init.contentType = @"video/x-m4v";
init.objectMeta = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"value1", @"x-oss-meta-name1", nil];
OSSTask * initTask = [client multipartUploadInit:init];
[initTask waitUntilFinished];
if (!initTask.error) {
OSSInitMultipartUploadResult * result = initTask.result;
MUuploadID = result.uploadId;
uploadputkey =objectKey;
NSLog(@"init multipart upload success: %@", result.uploadId);
}
else {
NSLog(@"multipart upload failed, error: %@", initTask.error);
return;
}
NSDictionary *pathsize = [self getVideoInfoWithSourcePath:FileURL];
NSInteger videoSize = [pathsize[@"size"]integerValue];
//要上传的文件
chuckCount = 100; // 10块 好区分图谱
//分片上传数量
uint64_t offset = videoSize/chuckCount;
for (int i = 1; i <= chuckCount; i++) {
@autoreleasepool {
OSSUploadPartRequest * uploadPart = [OSSUploadPartRequest new];
uploadPart.bucketName = uploadToBucket;
uploadPart.objectkey = uploadObjectkey;
uploadPart.uploadId = uploadId;
uploadPart.partNumber = i; // part number start from 1
NSString * docDir = FileURL;
uploadPart.uploadPartFileURL = [NSURL URLWithString:docDir];
NSFileHandle* readHandle = [NSFileHandle fileHandleForReadingAtPath:FileURL];
[readHandle seekToFileOffset:offset * (i -1)];
NSData* data = [readHandle readDataOfLength:offset];
uploadPart.uploadPartData = data;
// uploadPart.uploadPartProgress = ^(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) {
// NSLog(@"%lld %lld %lld", bytesSent, totalBytesSent, totalBytesExpectedToSend);
// dispatch_async(dispatch_get_main_queue(), ^{
// float gress = (double)totalBytesSent/totalBytesExpectedToSend;
// if([self.dclickDelegate respondsToSelector:@selector(WaveProgressPlus:)]){
// [self.dclickDelegate performSelector:@selector(WaveProgressPlus:) withObject:@(gress)];
// NSLog(@"gress123上传中");
// }
// });
// };
OSSTask * uploadPartTask = [client uploadPart:uploadPart];
[uploadPartTask waitUntilFinished];
if (!uploadPartTask.error) {
OSSUploadPartResult *result = uploadPartTask.result;
uint64_t fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:uploadPart.uploadPartFileURL.absoluteString error:nil] fileSize];
[partInfos addObject:[OSSPartInfo partInfoWithPartNum:i eTag:result.eTag size:fileSize]];
[self listParts:MUuploadID PartNumber:uploadPart.partNumber];
}
else {
NSLog(@"upload part error: %@", uploadPartTask.error);
return;
}
}
}
// OSSCompleteMultipartUploadRequest * complete = [OSSCompleteMultipartUploadRequest new];
// complete.bucketName = uploadToBucket;
// complete.objectKey = uploadObjectkey;
// complete.uploadId = uploadId;
// complete.partInfos = partInfos;
//
// OSSTask * completeTask = [client completeMultipartUpload:complete];
//
// [completeTask waitUntilFinished];
//
// if (!completeTask.error) {
// NSLog(@"multipart upload success!");
//
// } else {
// NSLog(@"multipart upload failed, error: %@", completeTask.error);
// return;
// }
}
#pragma mark 罗列分块
- (void)listParts:(NSString *)uploadId PartNumber:(int)partNumber{
OSSListPartsRequest * listParts = [OSSListPartsRequest new];
listParts.bucketName = bucketName;
listParts.objectKey = uploadputkey;
listParts.uploadId = uploadId;
OSSTask * listPartTask = [client listParts:listParts];
[listPartTask continueWithBlock:^id(OSSTask *task) {
if (!task.error) {
NSLog(@"list part result success!");
OSSListPartsResult * listPartResult = task.result;
dispatch_async(dispatch_get_main_queue(), ^{
float gress = (double)partNumber/chuckCount;
if([self.dclickDelegate respondsToSelector:@selector(WaveProgressPlus:)]){
[self.dclickDelegate performSelector:@selector(WaveProgressPlus:) withObject:@(gress)];
NSLog(@"gress123上传中");
}
if (gress == 1.0) {
if([self.dclickDelegate respondsToSelector:@selector(dissbackView)]){
[self.dclickDelegate performSelector:@selector(dissbackView)];
}
[self signAccessObjectURL];
}
});
NSLog(@"listPartResult is%@",listPartResult);
for (NSDictionary * partInfo in listPartResult.parts) {
NSLog(@"partInfoeachpart: %@", partInfo);
}
}
else {
NSLog(@"list part result error: %@", task.error);
}
return nil;
}];
}
#pragma mark - 断点续传
- (void)resumableUpload:(NSString *)FileURL objectKey:(NSString *)objectKey{
__block NSString * recordKey;
// NSString * docDir = [self getDocumentDirectory];
NSString * filePath =FileURL;
// NSString * bucketName = bucketName;
// objectKey = @"uploadKey";
[[[[[[OSSTask taskWithResult:nil] continueWithBlock:^id(OSSTask *task) {
// 为该文件构造一个唯一的记录键
NSURL * fileURL = [NSURL fileURLWithPath:filePath];
NSDate * lastModified;
NSError * error;
[fileURL getResourceValue:&lastModified forKey:NSURLContentModificationDateKey error:&error];
if (error) {
return [OSSTask taskWithError:error];
}
recordKey = [NSString stringWithFormat:@"%@-%@-%@-%@", bucketName, objectKey, [OSSUtil getRelativePath:filePath], lastModified];
// 通过记录键查看本地是否保存有未完成的UploadId
NSUserDefaults * userDefault = [NSUserDefaults standardUserDefaults];
return [OSSTask taskWithResult:[userDefault objectForKey:recordKey]];
}] continueWithSuccessBlock:^id(OSSTask *task) {
if (!task.result) {
// 如果本地尚无记录,调用初始化UploadId接口获取
OSSInitMultipartUploadRequest * initMultipart = [OSSInitMultipartUploadRequest new];
initMultipart.bucketName = bucketName;
initMultipart.objectKey = objectKey;
initMultipart.contentType = @"video/x-m4v";
return [client multipartUploadInit:initMultipart];
}
OSSLogVerbose(@"An resumable task for uploadid: %@", task.result);
return task;
}] continueWithSuccessBlock:^id(OSSTask *task) {
NSString * uploadId = nil;
if (task.error) {
return task;
}
if ([task.result isKindOfClass:[OSSInitMultipartUploadResult class]]) {
uploadId = ((OSSInitMultipartUploadResult *)task.result).uploadId;
}
else {
uploadId = task.result;
}
if (!uploadId) {
return [OSSTask taskWithError:[NSError errorWithDomain:OSSClientErrorDomain code:OSSClientErrorCodeNilUploadid userInfo:@{OSSErrorMessageTOKEN: @"Can't get an upload id"}]];
}
// 将“记录键:UploadId”持久化到本地存储
NSUserDefaults * userDefault = [NSUserDefaults standardUserDefaults];
[userDefault setObject:uploadId forKey:recordKey];
[userDefault synchronize];
return [OSSTask taskWithResult:uploadId];
}] continueWithSuccessBlock:^id(OSSTask *task) {
// 持有UploadId上传文件
_resumableUpload = [OSSResumableUploadRequest new];
_resumableUpload.bucketName = bucketName;
_resumableUpload.objectKey = objectKey;
_resumableUpload.uploadId = task.result;
_resumableUpload.uploadingFileURL = [NSURL fileURLWithPath:filePath];
_resumableUpload.uploadProgress = ^(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) {
NSLog(@"%lld %lld %lld", bytesSent, totalBytesSent, totalBytesExpectedToSend);
dispatch_async(dispatch_get_main_queue(), ^{
float gress = (double)totalBytesSent/totalBytesExpectedToSend;
if([self.dclickDelegate respondsToSelector:@selector(WaveProgressPlus:)]){
[self.dclickDelegate performSelector:@selector(WaveProgressPlus:) withObject:@(gress)];
NSLog(@"gress123上传中");
}
});
};
return [client resumableUpload:_resumableUpload];
}] continueWithBlock:^id(OSSTask *task) {
if (task.error) {
if ([task.error.domain isEqualToString:OSSClientErrorDomain] && task.error.code == OSSClientErrorCodeCannotResumeUpload) {
// 如果续传失败且无法恢复,需要删除本地记录的UploadId,然后重启任务
[[NSUserDefaults standardUserDefaults] removeObjectForKey:recordKey];
}
} else {
NSLog(@"%@",task.result);
NSLog(@"%@",self->_resumableUpload.objectKey);
NSLog(@"upload completed!");
uploadputkey = _resumableUpload.objectKey;
// 上传成功,删除本地保存的UploadId
[[NSUserDefaults standardUserDefaults] removeObjectForKey:recordKey];
NSLog(@"upload object success!");
if([self.dclickDelegate respondsToSelector:@selector(dissbackView)]){
[self.dclickDelegate performSelector:@selector(dissbackView)];
}
[self signAccessObjectURL];
}
return nil;
}];
}
-(void)cannelUpdate{
[_resumableUpload cancel];
// _resumableUpload = nil;
NSLog(@"取消网络操作");
// [OSSTask cancelledTask];
}
#pragma mark - 检查数据大小
- (NSDictionary *)getVideoInfoWithSourcePath:(NSString *)path{
NSLog(@"%@",path);
AVURLAsset * asset = [AVURLAsset assetWithURL:[NSURL fileURLWithPath:path]];
CMTime time = [asset duration];
int seconds = ceil(time.value/time.timescale);
NSInteger fileSize = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil].fileSize;
return @{@"size" : @(fileSize), @"duration" : @(seconds)};
}
参考文章:
https://mp.weixin.qq.com/s/Ru2L_z0Uc-B_aUuC7_UYvw
https://www.cnblogs.com/tangyuanby2/p/8358925.html
https://blog.csdn.net/weixin_39833509/article/details/88603957
https://blog.csdn.net/qq_30513483/article/details/82733071