iOS:将 .jpg 图像作为 base64 发送到 tcp 服务器

标签 ios objective-c xcode nsstream nsoutputstream

这个要杀了我。除了这个愚蠢的问题外,我已经很接近完成这项工作了。而且我不确定我是否能够充分描述这个问题,但我会尝试。

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

base64文件创建的时候,先保存到手机磁盘,因为可能图片比较多。然后当准备好发送时,该进程将读取每个 base64 文件并一次发送一个。

base64 函数创建的文本字符串非常大,起初它只发送大约 131,000 个字节,这很容易转换回二进制,但会渲染大约 1/4 到 1/3 的图像。我只是认为数据被截断了,因为该应用程序试图超越自己。

然后我找到了一个很好的片段,它向我展示了如何使用 NSStreamEventHasSpaceAvailable 事件将 base64 字符串拆分为几个 block 并按顺序发送它们。 ( 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 控件。)

网络流事件处理程序代码位于 Root View Controller 中,但 base64 中的图像数据是从另一个 View Controller 发送的。我的直觉告诉我这不是问题,因为到目前为止它工作得很好,但字符串要短得多。

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

服务器被编程为在看到正确的字节数时保存文件,但在应用程序关闭之前永远不会发生这种情况。我通过在服务器上使用 WireShark 知道正在接收一些数据,但正如我所说,其余数据在应用程序关闭之前不会到达。

我怀疑最后一部分(完成转移)是问题,但对于我的生活,我在网上找不到任何解决这个问题的东西,除非我太无知,不知道要使用什么搜索词......这很有可能。

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

最佳答案

编辑

问题的解决方案似乎与原始答案中的怀疑不同。通过评论中的讨论,QP 已经找到了解决方案。这是一个摘要:

通过原始 TCP 套接字传输数据需要彻底处理入队逻辑,而 QP 没有正确考虑到这一点。我推荐使用socket库CocoaAsyncSocket它处理这部分任务,反过来又使 QP 找到了一个可行的解决方案。

原答案:

我猜NSString不能胜任这项任务。它是用来固定的,嗯,弦。

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

关于iOS:将 .jpg 图像作为 base64 发送到 tcp 服务器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14942449/

相关文章:

ios - ios 出现 Payumoney 网关错误

ios - GMSMapView clear 不清除内存

xcode - 使用 Swift Package Manager 时如何生成用于开发的 Xcode 项目文件

xcode - Swift 3 和 iOS 10 错误内存泄漏错误

objective-c - 将 Google Objective-C API 'GTL' 添加到 iPhone 项目

ios - 需要遍历另一个数组内部的数组

iphone - 删除动画完成后更新 TableView

ios - 在 iOS 上使用 Google OAuth 注销

objective-c - 在 obj-c 中与 NSArray 一起使用的长度混淆

iphone - 捕获视频而不显示实际的视频源