// // WSUploadloadSession.m // zhuxun // // Created by winsoft on 17/6/26. // // #import "WSUploadSession.h" #import "AsyncSocket.h" #import "DataInputStream.h" #import "DataOutputStream.h" #import "NSData+crc32.h" #import "NSData+MD5Digest.h" #import "NSDictionary+JSON.h" #import "NSString+Extension.h" #import "FileUtils.h" #define ChunkSize 8*1024 @interface WSUploadSession() @property (nonatomic , copy) NSString *fileName; @property (nonatomic, copy)NSString *filePath; @property (nonatomic, copy)NSString *uuid; //@property (nonatomic, assign)AttachType attachMentType; /*文件发送相关*/ @property (nonatomic, strong)AsyncSocket *asyncSocket; @property (nonatomic, strong)NSSet *runLoopModes; @property (nonatomic, copy)NSMutableData *sendData; @property (nonatomic, copy)NSString *md5; @property (nonatomic, assign)unsigned long long fileSize; //初始偏移量 @property (nonatomic, assign)unsigned long long uploadOffset; @property (nonatomic , copy) NSString *host; @property (nonatomic , copy) NSString *port; @end @implementation WSUploadSession + (WSUploadSession *)uploadFileWithServerHost:(NSString *)host port:(NSString *)port fileMD5:(NSString *)fileMD5 uuid:(NSString *)uuid fileSize:(NSInteger)fileSize startSize:(NSInteger)startSize localFileFullPath:(NSString *)localFileFullPath processBlock:(UploadProgressBlock)processBlock successBlock:(UploadSuccessBlock)successBlock failBlock:(UploadFailedBlock)failBlock { WSUploadSession *uploadSession = [[WSUploadSession alloc]initUploadFileWithServerHost:host port:port fileMD5:fileMD5 uuid:uuid fileSize:fileSize startSize:startSize localFileFullPath:localFileFullPath processBlock:processBlock successBlock:successBlock failBlock:failBlock]; return uploadSession; } - (id)initUploadFileWithServerHost:(NSString *)host port:(NSString *)port fileMD5:(NSString *)fileMD5 uuid:(NSString *)uuid fileSize:(NSInteger)fileSize startSize:(NSInteger)startSize localFileFullPath:(NSString *)localFileFullPath processBlock:(UploadProgressBlock)processBlock successBlock:(UploadSuccessBlock)successBlock failBlock:(UploadFailedBlock)failBlock { if (self = [super init]) { self.uploadProgressBlock = processBlock; self.uploadFailedBlock = failBlock; self.uploadSuccessBlock = successBlock; if ([_asyncSocket isConnected]) { [_asyncSocket disconnect]; _asyncSocket.delegate = nil; _asyncSocket = nil; } _asyncSocket = [[AsyncSocket alloc] initWithDelegate:self]; _runLoopModes = [NSSet setWithObject:NSRunLoopCommonModes]; _uploadOffset = startSize; if ([FileUtils fileSizeAtPath:localFileFullPath] < fileSize) { fileSize = [FileUtils fileSizeAtPath:localFileFullPath]; }; _fileSize = fileSize; _filePath = localFileFullPath; _fileName = localFileFullPath.lastPathComponent; _host = host; _port = port; _md5 = fileMD5; _uuid = uuid; [self operationDidStart]; } return self; } //+ (void)networkRequestThreadEntryPoint:(id)__unused object { // @autoreleasepool { // [[NSThread currentThread] setName:@"TCPSocketUpLod"]; // NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; // [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; // [runLoop run]; // } //} // //+ (NSThread *)networkRequestThread { // static NSThread *_networkRequestThread = nil; // static dispatch_once_t oncePredicate; // dispatch_once(&oncePredicate, ^{ // _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil]; // [_networkRequestThread start]; // }); // return _networkRequestThread; //} // // // //-(void)start //{ // if ([self isCancelled]) { // [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[_runLoopModes allObjects]]; // } else if ([self isReady]) { // [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[_runLoopModes allObjects]]; // } //} //FILE_UP_SERVER_HOST FILE_UP_SERVER_PORT - (void)operationDidStart { NSError *error; if (![_asyncSocket connectToHost:_host onPort:[_port integerValue] error:&error]) { NSLog(@"Error connecting: %@", error); } } #pragma mark -- #pragma mark -- sockectOperate -(void)writeToSocket:(NSMutableData *)data { [_asyncSocket writeData:data withTimeout:-1 tag:0]; [_asyncSocket readDataToData:[AsyncSocket JsonData] withTimeout:-1 tag:1]; } #pragma mark socket delegate - (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port { NSLog(@"didConnectToHost");//通过数据包的偏移位置来控制断点上传的二进制数据 00112000231 NSData *bitData = [self makeChunk:_uploadOffset]; if (bitData) { [self writeToSocket:[[NSMutableData alloc]initWithData:[self makeChunk:_uploadOffset]]]; } } #warning 文件上传/下载只有两种状态,取消,下载 -(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{ //如果用户退出,如何判断消息是上次还是下载,则msg新增字段,这里为上传。 DataInputStream *inputData = [DataInputStream dataInputStreamWithData:data]; NSString *str = [inputData readUTF]; if (!str) { [self cancerUpload]; if (self.uploadFailedBlock) { self.uploadFailedBlock(@"error",self.uuid); } // if (self.uploadFailedDBBlock) { // self.uploadFailedDBBlock(@"error",self.uuid); // } return; } NSDictionary* dic = str.jsonValue; BOOL success = [[dic objectForKey:@"success"] boolValue]; NSInteger code = [[dic valueForKey:@"code"] integerValue]; if (!success) { NSString *errMessage = [dic objectForKey:@"message"]; NSLog(@"message:---%@", errMessage); [self cancerUpload]; if (self.uploadFailedBlock) { self.uploadFailedBlock(errMessage,self.uuid); } return; } //区分状态码的返回结果 if (code == 304) { // if (self.uploadSuccessBlock) { self.uploadSuccessBlock(nil,_fileSize,self.md5); } // if (self.uploadSuccessDBBlock) { // self.uploadSuccessDBBlock(nil,_fileSize,self.md5); // } // if (self.completionBlock) { // self.completionBlock(); // } //关闭连接 [sock disconnect]; return; }else if(code == 200){ unsigned long long start = [[dic valueForKey:@"accept"] integerValue]; double progress = ((double)start/_fileSize); if (self.uploadProgressBlock) { self.uploadProgressBlock(progress,start,self.uuid); NSLog(@"当前人物uuid--%@",self.uuid); } // if (self.uploadProgressDBBlock) { // self.uploadProgressDBBlock(progress,start,self.uuid); // } _uploadOffset = start; NSLog(@"已经上传%llu, 当前进度%f%%", start, progress*100); [self writeToSocket:[[NSMutableData alloc]initWithData:[self makeChunk:start]]]; } } -(void)onSocketDidDisconnect:(AsyncSocket *)sock { NSLog(@"socketDidDisconnect"); } -(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err { NSLog(@"socketDidDisconnect with error"); [self cancerUpload]; if (self.uploadFailedBlock) { self.uploadFailedBlock(@"onSocketDidDisconnect",self.uuid); } } #pragma mark -- #pragma mark -- /*客户端向服务端发送文件: 本次传输的包总长度(0-4) +协议说明JSON长度(4-8) { "md5":"E96261B90BE7B1895E9193D9CB9AAC5A", "crc":"F9C19B6F", "cmd":"1", "fileName":"0483737.xls", "offset":0, "user": { "sender":"serverid-gyj","receiver":"serverid-joe" }, "fileLength":83456, "client": { "name":"realicq", "version":"6.5" }, "complete":1 } +文件流长度(4位)+文件流 */ -(NSData *)makeChunk:(unsigned long long) start{ NSUInteger toSendChunkSize = 0; if (start + ChunkSize > _fileSize) { toSendChunkSize = (unsigned int)(_fileSize - start); }else{ toSendChunkSize = ChunkSize; } NSData *chunk = [[NSMutableData alloc] init]; if (_sendData && !_filePath) { [self cancerUpload]; if (self.uploadFailedBlock) { self.uploadFailedBlock(@"upload fail",self.uuid); } return nil; }else{ NSFileHandle *readHandle = [NSFileHandle fileHandleForReadingAtPath:_filePath]; [readHandle seekToFileOffset:start]; if(!readHandle) { [self cancerUpload]; if (self.uploadFailedBlock) { self.uploadFailedBlock(@"error",self.uuid); } return nil; } NSInputStream *inputStream = [NSInputStream inputStreamWithData:[readHandle readDataOfLength:toSendChunkSize]]; [inputStream open]; chunk = [[NSMutableData alloc]initWithLength:toSendChunkSize]; NSUInteger readLength = (NSUInteger)[inputStream read:(uint8_t*)[chunk bytes] maxLength:chunk.length]; if (readLength != [chunk length]) { [self cancerUpload]; if (self.uploadFailedBlock) { self.uploadFailedBlock(@"error",self.uuid); } return nil; } [inputStream close]; } NSString *crc32 = [NSString stringWithFormat:@"%@",[[NSString alloc] initWithFormat:@"%08x",chunk.crc32]]; NSDictionary *chunkDes = nil; // if (self.messageType == MESSAGE_TYPE_GROUP) { chunkDes = @{ @"md5":_md5, @"crc":crc32, @"cmd":@"1", @"fileName":_fileName, @"offset":@(start), @"fileLength":@(_fileSize), @"client":@{@"name":@"iOS",@"version":@"1.0"}, @"complete":@(1) }; NSLog(@"chunkDes: %@",chunkDes); NSString *desString=[chunkDes jsonString]; DataOutputStream *outData = [DataOutputStream new]; [outData writeInt:0]; [outData writeUTF:desString]; [outData writeBytes:chunk]; [outData writeDataCount]; NSData *sendData = [outData toByteArray]; if(!sendData) { [self cancerUpload]; if (self.uploadFailedBlock) { self.uploadFailedBlock(@"error",self.uuid); } } return sendData; } - (void)cancerUpload { // [super cancel]; if (_asyncSocket.isConnected) { [_asyncSocket disconnect]; _asyncSocket.delegate = nil; _asyncSocket = nil; } } - (void)resumeUpload { } @end