这个会杀了我。除了这个愚蠢的问题之外,我已经接近完成了这项工作。而且我不确定我是否能够充分描述问题,但我会尝试。
我的企业应用程序使用iPhone相机拍摄我们的现场人员购买的收据。我使用了一个非常酷的API来将jpeg数据转换为base 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的重要性是什么。某处有最大缓冲区大小?
使用各种NSLog和断点,我几乎已经确定数据是以这种方式离开手机,而不是被服务器扰乱。我还在一个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 :(得分:1)
修改强>
问题的解决方案似乎与原始答案中所怀疑的不同。通过评论中的讨论,QP已经成为一个解决方案。以下是摘要:
通过原始TCP套接字传输数据需要彻底处理入队逻辑,QP没有正确考虑这一点。我建议使用套接字库CocoaAsyncSocket
来处理这部分任务,从而使QP成为可行的解决方案。
原始答案:
我猜NSString
不能胜任这项任务。它是用来保持弦乐的。
尝试直接将文件读入NSData
并将其作为二进制文件发送。 (它的ascii到底是不是?)此外,这将比你当前的代码更加资源友好。