2013-02-18 83 views
0

这一个会杀了我。除了这个愚蠢的问题之外,我很接近完成这个任务。我不确定我能够充分描述这个问题,但我会尝试。iOS:将.jpg图像作为base64发送给tcp服务器

我的企业应用程序使用iPhone相机为我们的现场工作人员购买的收据拍照。我使用了一个非常酷的API将jpeg数据转换为64位(https://github.com/nicklockwood/Base64),通过TCP连接发送到VB 2010服务器,该服务器将其读取为文本字符串并将其转换回二进制文件。

在创建中的Base64文件,它首先保存到磁盘在手机上,因为可能会有更多的图像。然后准备发送时,该进程将读取每个base64文件并一次发送一个。

通过的base64函数创建的文本字符串是相当大的,起初它只是发送约131,000字节,这将转换回二进制轻松不够好,但会使大约1/4到1/3的图像。我刚才发现数据被截断,因为应用程序试图超越自己。

于是我找到了一个很好的片段,教我如何使用NSStreamEventHasSpaceAvailable事件中的Base64字符串分割成若干块,按顺序发送。 (http://www.ios-developer.net/iphone-ipad-programmer/development/tcpip/tcp-client)在发送完整文件时效果很好 - 也就是说,服务器接收到的结果文件的大小正确,与发送之前的base64文件相同。

这里的问题是,在某些时候由服务器接收到的文件已损坏,因为它似乎在文件的开头一切重新开始......换句话说,数据开始重演。

奇怪的是,重复部分每次都会在接收文件中的相同位置开始:位置131016.它不会在文件末尾开始重复,它只会在该位置中断文件点并跳回到开头。碰巧那是在我开始使用HasSpaceAvailable事件之前发送的文件的大小。我无法弄清楚价值131,016的重要性。某处的最大缓冲区大小?

使用各种NSLogs和断点的,我非常确定数据在离开电话这种方式,而不是由服务器加密。我还在一个NSMailComposeViewer方法中写道,它会将base64文件作为附件通过电子邮件发送给我,并且它完美地通过了。

下面是当从磁盘中读取该文件并发送到服务器的代码:

int i; 
    for (i = 0; i < [imageList count];i++){ 
    NSArray *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 
    NSString *documentsDirectory= [documentsPath objectAtIndex:0]; //Get the docs directory 
    NSString *imagePath = [documentsDirectory stringByAppendingPathComponent:imageFileName]; 
    imageFileName = [imageList objectAtIndex:i] ; 
    NSLog(@"Image index: %d - image file name: %@",i,imageFileName); 

    BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:imagePath]; 
    if(fileExists == YES){ 
     NSString *imageReceipt = [NSString stringWithContentsOfFile:imagePath encoding:NSASCIIStringEncoding error:nil]; 
     int32_t imageStringLen = [imageReceipt length]; 
     NSString *imageSize = [NSString stringWithFormat: @"%d",imageStringLen]; 
     NSString *currentImage = [NSString stringWithFormat:@"image,%@,%@,%@",imageFileName,imageSize,imageReceipt]; //creates a CSV string with a header string with the filename and file size, and then appends the image data as the final comma-separated string. 
     data = [[NSMutableData alloc] initWithData:[currentImage dataUsingEncoding:NSASCIIStringEncoding]]; 

     [outputStream write:[data bytes] maxLength:[data length]]; 

然后在这里是使用HasSpaceAvailable事件的代码:

 case NSStreamEventHasSpaceAvailable: 
     if (data != nil) 
     { 
      //Send rest of the packet 
      int ActualOutputBytes = [outputStream write:[data bytes] maxLength:[data length]]; 
      int totalLength = [data length]; 

      if (ActualOutputBytes >= totalLength) 
      { 
       //It was all sent 
       data = nil; 

      } 
      else 
      { 
       //Only partially sent 
       [data replaceBytesInRange:NSMakeRange(0, ActualOutputBytes) withBytes:NULL length:0];  //Remove sent bytes from the start 
      } 
     } 

     break; 

(我特别是这样的代码,因为它允许放置一个ProgressView控制在屏幕上。)

网络流事件处理程序代码是在根视图控制器,但图像base64中的数据正从另一个视图控制器发送。我的直觉告诉我,这不是一个问题,因为它直到现在都运行良好,但是使用更短的字符串。

现在,还有一个可能相关的问题 - 可能是。除非我关闭应用程序,否则我似乎无法完成数据的传输。我猜,服务器在连接关闭之前不会看到它。我曾尝试将[outputStream close]放置在代码中的各个位置,但无济于事。我也尝试用换行或回车或两者都终止base64字符串。

服务器被编程为当文件看到正确的字节数时保存该文件,但在应用程序关闭之前永远不会发生。我知道通过在服务器上使用WireShark正在接收一些数据,但正如我所说的,剩下的数据直到应用程序关闭时才会到达。我怀疑这最后一部分(完成传输)是问题,但对于我的生活,我找不到任何网上解决这个问题的东西,除非我太无知,不知道要使用哪些搜索条件....很有可能。

我希望我已经提供了足够的信息。谁能帮我?

+0

当计算边界的大小(131016)时,我忽略了考虑头字节。头文件将56个字节添加到文件中,使整个传输(截断之前)为131072,即2^17。我仍然不知道它的意义。这是我的理解,NSData可以容纳多达2GB。 – 2013-02-18 19:51:06

回答

1

编辑

的解决问题的办法似乎比什么被怀疑在原来的答案不同。通过评论中的讨论,QP得到了解决方案。下面是一个总结:

通过原始TCP套接字传输数据需要彻底处理enqueing逻辑,QP没有适当考虑。我建议使用套接字库CocoaAsyncSocket,它负责处理这部分任务,并将QP引导至一个可行的解决方案。

原来的答案:

我猜NSString是达不到的任务。它是用来保持弦的。

尝试直接将文件读入NSData,并将其作为二进制文件发送。 (它最后的ascii,不是吗?)另外,这比你当前的代码更加资源友好。

+0

感谢您的回复。我一直在玩这个主意,但我不得不怀疑:NSString会在什么时候不足? base64转换程序将数据作为NSString返回,并且该字符串在保存为文件时起作用,然后在从文件中检索并附加到电子邮件时起作用;而不是当它通过TCP发送时。一个人(以我最低水平的专业知识)会认为,如果NSString在这些地方工作,它应该工作到它被转换为NSData并通过网络发送的地步。 – 2013-02-19 17:05:59

+0

不要让我怀疑你。我只想知道在整个过程中避免使用NSString是否可取,尽管我不知道它是如何完成的。无论如何,我试图完全以NSData方式将数据从文件移动到outputStream,尽管行为有细微差别,但它仍然只发送部分数据。 – 2013-02-19 17:07:38

+1

是的,你说得对,我的猜测太过分了。第二次阅读后,我认识到你正在使用原始的tcp套接字。把它们排队是一件难以理顺的事情。所以我会考虑使用一个tcp套接字库来承担你的负担。看看[CocoaAsyncSocket](https://github.com/robbiehanson/CocoaAsyncSocket/)就是这么做的。 (它在公共领域,所以在你的项目中使用它应该没有问题。) – ilmiacs 2013-02-19 20:52:23

相关问题