大文件分片断点上传显示进度条

来源:互联网 发布:linux lnmp一键安装 编辑:程序博客网 时间:2024/05/17 01:40

项目里面刚做完文件这块,本人负责文件分片上传功能模块,现将实现思路以及关键实现点整理一下。


 文件分片断点上传思路如下:

第一步:分片上传

首先在本地沙盒Cache目录下创建一个专门存放上传文件的缓存文件夹Upload,检查Upload中是否有该文件(通过文件md5值+文件名找该路径),没有表示不是断点上传,在Upload中创建一个存放当前要上传文件的文件夹(命名规则为整个文件md5值+文件名),通过文件分片类获取该文件所有分片并写到分片文件夹中,分片命名跟其序列一致;有则表示是断点续传,把分片文件夹中所有分片用数组装着调用分片上传接口,在成功的回调里面删除该分片,直到所有分片上传完删除该文件夹。


第二部:合并上传到云端

这里两步合并(上传到服务器)、上传到云端均是调用接口不做赘述


核心部分分片上传代码:

在这里实现分片断点上传我用到了CKGFileUploadManager(文件上传管理单例)、CKGFileUploadCachedManager(文件上传缓存管理单例)、FileStreamOperation(文件操作流类,文件分片成分片数组)、CKGFileUploadRequest(抽出来的上传类,基于ASI) 


FileStreamOperation(文件操作流类,文件分片成分片数组)核心代码(这个类是在网上找的资料):

.h

#import <Foundation/Foundation.h>#define FileFragmentMaxSize         1024 *1024 // 1MB@class FileFragment;/** * 文件流操作类 */@interface FileStreamOperation : NSObject<NSCoding>@property (nonatomic, readonly, copy) NSString *fileName;//包括文件后缀名的文件名@property (nonatomic, readonly, assign) NSUInteger fileSize;//文件大小@property (nonatomic, readonly, copy) NSString *filePath;//文件所在的文件目录@property (nonatomic, readonly, strong) NSArray<FileFragment*> *fileFragments;//文件分片数组//若为读取文件数据,打开一个已存在的文件。//若为写入文件数据,如果文件不存在,会创建的新的空文件。(创建FileStreamer对象就可以直接使用fragments(分片数组)属性)- (instancetype)initFileOperationAtPath:(NSString*)path forReadOperation:(BOOL)isReadOperation ;//获取当前偏移量- (NSUInteger)offsetInFile;//设置偏移量, 仅对读取设置- (void)seekToFileOffset:(NSUInteger)offset;//将偏移量定位到文件的末尾- (NSUInteger)seekToEndOfFile;//关闭文件- (void)closeFile;#pragma mark - 读操作//通过分片信息读取对应的片数据- (NSData*)readDateOfFragment:(FileFragment*)fragment;//从当前文件偏移量开始- (NSData*)readDataOfLength:(NSUInteger)bytes;//从当前文件偏移量开始- (NSData*)readDataToEndOfFile;#pragma mark - 写操作//写入文件数据- (void)writeData:(NSData *)data;@end//上传文件片@interface FileFragment : NSObject<NSCoding>@property (nonatomic,copy)NSString          *fragmentId;    //片的唯一标识@property (nonatomic,assign)NSUInteger      fragmentSize;   //片的大小@property (nonatomic,assign)NSUInteger      fragementOffset;//片的偏移量@property (nonatomic,assign)BOOL            fragmentStatus; //上传状态 YES上传成功@end.m#import "FileStreamOperation.h"#import <CommonCrypto/CommonDigest.h>#pragma mark - FileStreamOperation@interface FileStreamOperation ()@property (nonatomic, copy) NSString                          *fileName;@property (nonatomic, assign) NSUInteger                      fileSize;@property (nonatomic, copy) NSString                          *filePath;@property (nonatomic, strong) NSArray<FileFragment*>          *fileFragments;@property (nonatomic, strong) NSFileHandle                    *readFileHandle;@property (nonatomic, strong) NSFileHandle                    *writeFileHandle;@property (nonatomic, assign) BOOL                            isReadOperation;@end@implementation FileStreamOperation+ (NSString *)fileKey {        CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);    CFStringRef cfstring = CFUUIDCreateString(kCFAllocatorDefault, uuid);    const char *cStr = CFStringGetCStringPtr(cfstring,CFStringGetFastestEncoding(cfstring));    unsigned char result[16];    CC_MD5( cStr, (unsigned int)strlen(cStr), result );    CFRelease(uuid);        return [NSString stringWithFormat:            @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%08lx",            result[0], result[1], result[2], result[3],            result[4], result[5], result[6], result[7],            result[8], result[9], result[10], result[11],            result[12], result[13], result[14], result[15],            (unsigned long)(arc4random() % NSUIntegerMax)];}- (void)encodeWithCoder:(NSCoder *)aCoder {        [aCoder encodeObject:[self fileName] forKey:@"fileName"];    [aCoder encodeObject:[NSNumber numberWithUnsignedInteger:[self fileSize]] forKey:@"fileSize"];    [aCoder encodeObject:[self filePath] forKey:@"filePath"];    [aCoder encodeObject:[self fileFragments] forKey:@"fileFragments"];}- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {    self = [super init];    if (self != nil) {        [self setFileName:[aDecoder decodeObjectForKey:@"fileName"]];        [self setFileSize:[[aDecoder decodeObjectForKey:@"fileSize"] unsignedIntegerValue]];        [self setFilePath:[aDecoder decodeObjectForKey:@"filePath"]];        [self setFileFragments:[aDecoder decodeObjectForKey:@"fileFragments"]];    }        return self;}- (BOOL)getFileInfoAtPath:(NSString*)path {        NSFileManager *fileMgr = [NSFileManager defaultManager];    if (![fileMgr fileExistsAtPath:path]) {        NSLog(@"文件不存在:%@",path);        return NO;    }        self.filePath = path;        NSDictionary *attr =[fileMgr attributesOfItemAtPath:path error:nil];    self.fileSize = attr.fileSize;        NSString *fileName = [path lastPathComponent];    self.fileName = fileName;        return YES;}// 若为读取文件数据,打开一个已存在的文件。// 若为写入文件数据,如果文件不存在,会创建的新的空文件。- (instancetype)initFileOperationAtPath:(NSString*)path forReadOperation:(BOOL)isReadOperation {        if (self = [super init]) {        self.isReadOperation = isReadOperation;        if (_isReadOperation) {            if (![self getFileInfoAtPath:path]) {                return nil;            }            self.readFileHandle = [NSFileHandle fileHandleForReadingAtPath:path];            [self cutFileForFragments];        } else {            NSFileManager *fileMgr = [NSFileManager defaultManager];            if (![fileMgr fileExistsAtPath:path]) {                [fileMgr createFileAtPath:path contents:nil attributes:nil];            }                        if (![self getFileInfoAtPath:path]) {                return nil;            }                        self.writeFileHandle = [NSFileHandle fileHandleForWritingAtPath:path];        }    }        return self;}//- (instancetype)#pragma mark - 读操作//切分文件片段- (void)cutFileForFragments {        NSUInteger offset = FileFragmentMaxSize;    // 块数    NSUInteger chunks = (_fileSize%offset==0)?(_fileSize/offset):(_fileSize/(offset) + 1);        NSMutableArray<FileFragment *> *fragments = [[NSMutableArray alloc] initWithCapacity:0];    for (NSUInteger i = 0; i < chunks; i ++) {                FileFragment *fFragment = [[FileFragment alloc] init];        fFragment.fragmentStatus = NO;        fFragment.fragmentId = [[self class] fileKey];        fFragment.fragementOffset = i * offset;                if (i != chunks - 1) {            fFragment.fragmentSize = offset;        } else {            fFragment.fragmentSize = _fileSize - fFragment.fragementOffset;        }                [fragments addObject:fFragment];    }        self.fileFragments = fragments;}//通过分片信息读取对应的片数据- (NSData*)readDateOfFragment:(FileFragment*)fragment {        if (fragment) {        [self seekToFileOffset:fragment.fragementOffset];        return [_readFileHandle readDataOfLength:fragment.fragmentSize];    }        return nil;}- (NSData*)readDataOfLength:(NSUInteger)bytes {    return [_readFileHandle readDataOfLength:bytes];}- (NSData*)readDataToEndOfFile {    return [_readFileHandle readDataToEndOfFile];}#pragma mark - 写操作// 写入文件数据- (void)writeData:(NSData *)data {    [_writeFileHandle writeData:data];}#pragma mark - common// 获取当前偏移量- (NSUInteger)offsetInFile{    if (_isReadOperation) {        return [_readFileHandle offsetInFile];    }        return [_writeFileHandle offsetInFile];}// 设置偏移量,仅对读取设置- (void)seekToFileOffset:(NSUInteger)offset {    [_readFileHandle seekToFileOffset:offset];}// 将偏移量定位到文件的末尾- (NSUInteger)seekToEndOfFile{    if (_isReadOperation) {        return [_readFileHandle seekToEndOfFile];    }        return [_writeFileHandle seekToEndOfFile];}// 关闭文件- (void)closeFile {    if (_isReadOperation) {        [_readFileHandle closeFile];    } else {        [_writeFileHandle closeFile];    }}@end#pragma mark - FileFragment@implementation FileFragment- (void)encodeWithCoder:(NSCoder *)aCoder {        [aCoder encodeObject:[self fragmentId] forKey:@"fragmentId"];    [aCoder encodeObject:[NSNumber numberWithUnsignedInteger:[self fragmentSize]] forKey:@"fragmentSize"];    [aCoder encodeObject:[NSNumber numberWithUnsignedInteger:[self fragementOffset]] forKey:@"fragementOffset"];    [aCoder encodeObject:[NSNumber numberWithUnsignedInteger:[self fragmentStatus]] forKey:@"fragmentStatus"];}- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {    self = [super init];    if (self != nil) {        [self setFragmentId:[aDecoder decodeObjectForKey:@"fragmentId"]];        [self setFragmentSize:[[aDecoder decodeObjectForKey:@"fragmentSize"] unsignedIntegerValue]];        [self setFragementOffset:[[aDecoder decodeObjectForKey:@"fragementOffset"] unsignedIntegerValue]];        [self setFragmentStatus:[[aDecoder decodeObjectForKey:@"fragmentStatus"] boolValue]];    }        return self;}


CKGFileUploadRequest(抽出来的上传类,基于ASI)代码:

.h

#import <Foundation/Foundation.h>#import "CKGFileModel.h"#import "FileStreamOperation.h"#import "Message.h"//分片上传成功block回调typedef void(^FragmentsUploadFinished)(CKGFileModel *fd, NSArray *fragments);@interface CKGFileUploadRequest : NSObject@property (nonatomic, copy)FragmentsUploadFinished finishedBlock;//上传请求方法- (instancetype)initWithFragments:(NSArray *)fragments file:(CKGFileModel *)file fileStreamer:(FileStreamOperation *)fileStreamer isChatSend:(BOOL)isChatSend message:(Message *)msg;@end



.m

#import "CKGFileUploadRequest.h"#import "CKGFileUploadManager.h"#import "CKGFileUploadCachedManager.h"@implementation CKGFileUploadRequest- (instancetype)initWithFragments:(NSArray *)fragments file:(CKGFileModel *)file fileStreamer:(FileStreamOperation *)fileStreamer isChatSend:(BOOL)isChatSend message:(Message *)msg{    self = [super init];    if (self) {        __block float uploadCount = 0.0;        NSInteger fileFragmentCount = fragments.count;        for (int i = 0 ; i<fragments.count; i++) {            NSString *urlString = [BASE_URL stringByAppendingString:@"/api/upload/formFile"];            NSString *myId = [UD objectForKey:UID_KEY];                        NSDictionary *postData = @{                                       @"uid":CheckStringValue(myId),                                       @"act":CheckStringValue(@"upload"),                                       @"name":CheckStringValue(file.fileName),                                       @"ext":CheckStringValue(file.fileExt),                                       @"size":CheckStringValue(file.size),                                       @"trunk_count":@(fileFragmentCount),                                       @"trunk_id":@(i+1),                                       @"md5":CheckStringValue(file.md5Str)                                       };            ASIFormDataRequest *__weak request = [CKGHttpBase requestWithURLString:urlString                                                                 andPostDictionary:postData];                        request.delegate = self;            //获取对应文件分片            __block NSData *data = [fileStreamer readDateOfFragment:fragments[i]];//表单填写            [request setData:data  withFileName:file.fileName andContentType:@"multipart/form-data" forKey:@"file"];                        [request startAsynchronous];            [request setCompletionBlock:^{                NSString *string = request.responseString;                NSDictionary *dataDic = [string objectFromJSONString];//                DLog(@" %zi dataDic:%@",i+1,dataDic);                if ([dataDic[@"result"] integerValue] == 1 && [dataDic[@"data"][@"file_exists"] integerValue] == 0) {                    uploadCount++;                    //分片上传成功从沙盒对应路径删除//                    [[CKGFileUploadCachedManager sharedInstance] removeFileByName:file.fileName fragment:i+1];                                        //更新进度条(这里通过block或者通知传到需要更新进度条进度的地方)                    DLog(@"%@ progress>>>>>>>>>>>%f",file.fileName,(float)(uploadCount/fileFragmentCount));                    if (file.isAsset) {                        if (isChatSend) {                            NSDictionary *dic = @{@"file":file,@"progress":@((float)(uploadCount * 1.0/fileFragmentCount)),@"message":msg};                            [NC postNotificationName:@"CHATSENDFILE" object:dic];                        }else                        {                            [CKGFileUploadManager sharedInstance].upProgressBlock(file,(float)(uploadCount/fileFragmentCount),msg);                        }                    }                                        if(uploadCount == fileFragmentCount)                    {                       //所有分片上传完毕删除对应文件//                        [[CKGFileUploadCachedManager sharedInstance] deleteDirctoryAtPath:[NSString stringWithFormat:@"%@%@",file.md5Str,file.fileName]];                        self.finishedBlock(file,fragments);                        uploadCount = 0.0;                    }                }            }];        }    }    return self;}



CKGFileUploadManager(文件上传管理单例)核心代码:

上传文件的方法及实现

.h

- (void)uploadWithFile:(CKGFileModel *)file VC:(UIViewController *)vc message:(Message *)message;



.m

#pragma mark - 上传功能- (void)uploadWithFile:(CKGFileModel *)file VC:(UIViewController *)vc message:(Message *)message{    _holdVC = vc;    //获取文件分片数组        fileStreamer = [[FileStreamOperation alloc] initFileOperationAtPath:[self getFilePath:file.fileName] forReadOperation:YES];    if (fileStreamer.fileFragments.count)    {      [self uploadWithFragment:fileStreamer.fileFragments file:file message:message];    }}//上传文件分片- (void)uploadWithFragment:(NSArray *)fragments file:(CKGFileModel *)file message:(Message *)message{    dispatch_group_t group = dispatch_group_create ();        //创建上传缓存路径   [[CKGFileUploadCachedManager sharedInstance] createUploadDirectoryPath];        //创建上传的文件路径 md5Str+fileName    [[CKGFileUploadCachedManager sharedInstance] createUploadFilePath:[NSString stringWithFormat:@"%@%@",file.md5Str,file.fileName]];        //检查该文件是否在本地沙河中存在,文件名格式为文件md5Str+fileName    NSString *filePath = [[CKGFileUploadCachedManager sharedInstance] getFilePath:[NSString stringWithFormat:@"%@%@",file.md5Str,file.fileName]];        NSArray *tempFragments;        if ([[CKGFileUploadCachedManager sharedInstance] fileIsExist:filePath]) {        //存在,说明是断点续传,只需要上传本地之外的文件分片        //获取还没上传的文件分片数组        tempFragments = [self getNotUploadedFragments:file];    }else    {        //不存在,整个文件分片都下载;        //所有分片写入到沙盒                for (int i=0; i<fragments.count;i++)        {            NSString *fragPath = [filePath stringByAppendingPathComponent:[NSString stringWithFormat:@"%d",i+1]];            NSData *data = [fileStreamer readDateOfFragment:fragments[i]];            [[CKGFileUploadCachedManager sharedInstance] writeFileWithData:data toPath:fragPath];        }        tempFragments = fragments;    }        dispatch_queue_t queue = dispatch_queue_create("fragment.queue", DISPATCH_QUEUE_CONCURRENT);        dispatch_async(queue, ^{//上传请求        CKGFileUploadRequest *fragmentRequest = [[CKGFileUploadRequest alloc] initWithFragments:fragments file:file fileStreamer:fileStreamer isChatSend:_isChatSend message:message];        fragmentRequest.finishedBlock = ^(CKGFileModel *fd,NSArray *fgts){            //合并请求            NSLog(@"********************%@分片上传完成",file.fileName);            [self combineWith:fgts file:fd message:message];        };    });}//上传文件分片,所有片都上传完毕后合并- (void)combineWith:(NSArray *)fragments file:(CKGFileModel *)file message:(Message *)message{    NSString *urlString = [BASE_URL stringByAppendingString:@"/api/upload/formFile"];    NSString *myId = [UD objectForKey:UID_KEY];    //    //文件名去掉后缀    NSMutableString *tempStr = [NSMutableString stringWithString:file.fileName];        NSString *extStr = [NSString stringWithFormat:@".%@",file.fileExt];        NSRange range = [tempStr rangeOfString:extStr.lowercaseString];        [tempStr deleteCharactersInRange:range];////    file.fileName = tempStr;    NSDictionary *postData = @{                               @"uid":CheckStringValue(myId),                               @"act":CheckStringValue(@"join"),                               @"name":CheckStringValue(tempStr),                               @"ext":CheckStringValue(file.fileExt),                               @"size":CheckStringValue(file.size),                               @"trunk_count":@(fragments.count),                               @"md5":CheckStringValue(file.md5Str)                               };    ASIFormDataRequest *__weak request = [CKGHttpBase requestWithURLString:urlString                                                         andPostDictionary:postData];        request.delegate = self;        [request startAsynchronous];    [request setCompletionBlock:^{        NSString *string = request.responseString;        NSDictionary *dataDic = [string objectFromJSONString];        DLog(@" bb dataDic:%@",dataDic);        if ([dataDic[@"result"] integerValue]==1 && [dataDic[@"data"][@"file_exists"] integerValue]==1) {            NSString *storageID = dataDic[@"data"][@"storage_id"];            NSDictionary *fileDic = @{@"storage_id":storageID,@"name":tempStr};            file.storageId = storageID;            file.fileUrl = dataDic[@"data"][@"url"];            //上传到云端            if (_isSend) {                self.sendBlock(file,storageID,message);            }else{                [self uploadToCloudWithFileDic:fileDic file:file];            }                    }    }];}#pragma mark - 上传到云端- (void)uploadToCloudWithFileDic:(NSDictionary *)fileDic file:(CKGFileModel *)file{    NSString *dir_id = _dir_id.length?_dir_id:@"0";    [CKGHttpManager cloudUploadFileWithUid:@""                                    dir_id:dir_id                                     files:fileDic                                  andBlock:^(BOOL resultBool, NSString *message, NSDictionary *data) {                                      _dir_id = @"0";                                      if (resultBool == 1) {                                    [NC postNotificationName:@"kMyFileCurrentVCRefresh" object:nil];                                     self.cloudUploadBlock(file,YES);                                      }else                                      {                                          [_holdVC.navigationController.view addHUDLabelView:[NSString stringWithFormat:@"%@上传失败",file.fileName] Image:nil afterDelay:1];                                          self.cloudUploadBlock(file,NO);                                      }                                  }];}


CKGFileUploadCachedManager(文件上传缓存管理单例)代码:
.h

#import <Foundation/Foundation.h>@interface CKGFileUploadCachedManager : NSObject{    NSFileManager *_fm;}+ (instancetype)sharedInstance;//判断文件是否存在- (BOOL)fileIsExist:(NSString *)fileName;//获取文件路径- (NSString *)getFilePath:(NSString *)fileName;//获取该文件路径下所有文件名- (NSArray *)getAllFileNameInPath:(NSString *)fileName;//创建上传路径- (void)createUploadDirectoryPath;//创建每个上传文件的文件夹- (void)createUploadFilePath:(NSString *)fileName;//写入文件分片到对应路径- (void)writeFileWithData:(NSData *)fragment toPath:(NSString *)filePath;//删除文件分片- (void)removeFileByName:(NSString *)fileName fragment:(NSInteger)fragmentIndex;//删除文件- (void)deleteDirctoryAtPath:(NSString *)filePath;@end


.m

#import "CKGFileUploadCachedManager.h"static CKGFileUploadCachedManager *fileManager = nil;@implementation CKGFileUploadCachedManager- (instancetype)init{    self = [super init];    if (self) {        _fm = [[NSFileManager alloc] init];;    }    return self;}+ (instancetype)sharedInstance{    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        if (!self) {            fileManager  = [[CKGFileUploadCachedManager alloc] init];        }    });    return fileManager;}#pragma mark - 是否存在该路径- (BOOL)fileIsExist:(NSString *)fileName{    //获取缓存目录下的路径    NSString *filePath = [self getFilePath:fileName];    return [_fm fileExistsAtPath:filePath];}#pragma mark - 获取文件路径- (NSString *)getFilePath:(NSString *)filename{    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);    NSString *cachesDirectory = [paths objectAtIndex:0];    NSString *lastUploadPath = [cachesDirectory stringByAppendingPathComponent:@"Upload"];    NSString *filePath = [lastUploadPath stringByAppendingPathComponent:filename];    return filePath;}#pragma mark - 获取该文件路径下所有文件名- (NSArray *)getAllFileNameInPath:(NSString *)fileName{    NSMutableArray *fileNames = [[NSMutableArray alloc] initWithCapacity:0];        NSString *path= [self getFilePath:fileName]; // 要列出来的目录        NSDirectoryEnumerator *myDirectoryEnumerator;        myDirectoryEnumerator=[_fm enumeratorAtPath:path];        //列举目录内容,可以遍历子目录        NSMutableString *subPath = [[NSMutableString alloc] initWithCapacity:0];        while((subPath=[myDirectoryEnumerator nextObject])!=nil)    {        NSLog(@"%@",subPath);        NSString *deleteStr = [NSString stringWithFormat:@"%@/",path];                [subPath deleteCharactersInRange:[subPath rangeOfString:deleteStr]];                [fileNames addObject:subPath];    }        return (NSArray *)fileNames;}#pragma mark - 创建UPLOAD根文件夹- (void)createUploadDirectoryPath{    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);    NSString *cachesDirectory = [paths objectAtIndex:0];    NSString *lastUploadPath = [cachesDirectory stringByAppendingPathComponent:@"Upload"];        BOOL fileExists = [_fm fileExistsAtPath:lastUploadPath];    if (!fileExists)    {//如果不存在则创建        [_fm createDirectoryAtPath:lastUploadPath       withIntermediateDirectories:YES                        attributes:nil                             error:nil];    }}#pragma mark - 创建每个上传文件的文件夹- (void)createUploadFilePath:(NSString *)fileName;{    NSString *lastUploadPath = [self getFilePath:fileName];    BOOL fileExists = [_fm fileExistsAtPath:lastUploadPath];    if (!fileExists)    {//如果不存在则创建        [_fm createDirectoryAtPath:lastUploadPath       withIntermediateDirectories:YES                        attributes:nil                             error:nil];    }    }#pragma mark - 写入文件分片- (void)writeFileWithData:(NSData *)fragment fragmentIndex:(NSInteger)fragmentIndex fileName:(NSString *)fileName{    NSString *tempPath = [[self getFilePath:fileName] stringByAppendingPathComponent:[NSString stringWithFormat:@"%zi",fragmentIndex]];    //写入    [fragment writeToFile:tempPath atomically:YES];}#pragma mark - 删除文件分片- (void)removeFileByName:(NSString *)fileName fragment:(NSInteger)fragmentIndex{    NSString *fragmentPath = [[self getFilePath:fileName] stringByAppendingPathComponent:[NSString stringWithFormat:@"%zi",fragmentIndex]];    DLog(@"删除分片路径************:%@",fragmentPath);    NSError *err = nil;    if (![_fm removeItemAtPath:fragmentPath error:&err])    {        NSLog(@"removeItem failed, err:%@", err);    }}#pragma mark - 删除文件- (void)deleteDirctoryAtPath:(NSString *)fileName{    NSString *filePath = [[self getFilePath:fileName] stringByAppendingPathComponent:fileName];    [_fm removeItemAtPath:filePath error:nil];}@end



以上就是本人实现文件分片断点上传的思路以及代码分享,由于水平有限,有问题的地方还请各位大神指正!



















CKGFileUploadCachedManager

CKGFileUploadCachedManager

CKGFileUploadCachedManager

1 0
原创粉丝点击