HTTP断点续传与断点上传之 -- 文件流操作
来源:互联网 发布:乐淘吧淘宝天猫商城 编辑:程序博客网 时间:2024/06/05 18:24
不管是下载还是上传,断点的时候,就需要对文件流进行精确的操作。
1、下载断开了,已经下载的数据保存到文件,再次继续下载的时候需要从文件的尾巴继续追加数据;
2、同理上传也是一样,http通信中有可能断开或者丢包的情况,就需要重传指定的文件片;
我封装的这个类,是基于把文件切成片的形式
FileStreamOperation.h
//// FileStreamOperation.h// CommonProject//// Created by wuyoujian on 16/7/6.// Copyright © 2016年 wuyoujian. All rights reserved.//#import <Foundation/Foundation.h>#define FileFragmentMaxSize 1024 * 512 // 512k@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;// 文件分片数组// 若为读取文件数据,打开一个已存在的文件。// 若为写入文件数据,如果文件不存在,会创建的新的空文件。- (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
FileStreamOperation.m
//// FileStreamOperation.m// CommonProject//// Created by wuyoujian on 16/7/6.// Copyright © 2016年 wuyoujian. All rights reserved.//#import "FileStreamOperation.h"#import <CommonCrypto/CommonDigest.h>//// 把FileStreamOpenration类保存到UserDefault中//static NSString *const UserDefaultFileInfo = @"UserDefaultFileInfo";#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;}#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;}@end
值得注意的地方,HTTP的断点续传HTTP协议是支持的,但是需要前后端配套开发。但断点上传就需要自定义的方式上传,HTTP协议是不支持的,所以也是需要前后端配套开发。
后面我会补充支持断点的网络通信那一块代码!
1 0
- HTTP断点续传与断点上传之 -- 文件流操作
- HTTP文件断点上传
- http文件断点上传
- HTTP文件断点上传
- HTTP文件断点上传
- HTTP文件断点上传
- HTTP文件断点上传
- HTTP文件断点上传
- java HTTP文件断点上传
- java http大文件断点续传上传
- 分片上传,断点续传,php文件操作,
- 实习记录之http断点上传
- http断点续传与文件下载原理解析
- 断点下载文件(断点续传)
- AIR文件上传与文件断点续传方式下载
- 断点续传下载文件 http
- 操作文件与上传
- xfire 断点上传文件
- Chain of Responsibility模式详解--设计模式(21)
- [android]三步实现从右向左输入的金额输入框EditText
- Asp.net中GridView使用详解(引)转
- postgresql数据类型varchar长度
- 【Android基础知识】Fragment设计哲学和加载方式
- HTTP断点续传与断点上传之 -- 文件流操作
- tcp socket客户端发送请求连接http服务
- Eclipse下更换主题-Sublime的主题
- 百度地图 聚合功能的实现
- Java设计模式之装饰模式
- 1029. Median (25)
- Android开发应用安装出现两个相同应用的解决办法
- apache开启页面压缩
- source insight研究——正则表达式篇(转)