Question

This one is going to kill me. I'm so close to getting this done except for this one stupid problem. And I am not sure I will be able to adequately describe the problem, but I'll try.

My enterprise app uses the iPhone camera to take pictures of receipts of purchases made by our field personnel. I used a real cool API for turning the jpeg data to base 64 (https://github.com/nicklockwood/Base64) to send via TCP connection to the VB 2010 server, which reads it as a text string and converts it back to a binary.

When the base64 file is created, it is first saved to disk on the phone, because there may be more images. Then when ready to send, the process will read each base64 file and send it one at a time.

The text string created by the base64 function is quite large, and at first it was only sending about 131,000 bytes, which would convert back to binary easily enough but would render about 1/4 to 1/3 of the image. I just figured that the data was being truncated because the app was trying to get ahead of itself.

So then I found a nice snippet that showed me how to use the NSStreamEventHasSpaceAvailable event to split the base64 string into several chunks and send them sequentially. (http://www.ios-developer.net/iphone-ipad-programmer/development/tcpip/tcp-client) That works great insofar as it sends the full file -- that is, the resulting file received by the server is the correct size, the same as the base64 file before it's sent.

The problem here is that at some point the file received by the server is corrupted because it seems to start all over at the beginning of the file... in other words, the data starts to repeat itself.

The odd part is that the repeating part starts at exactly the same spot in the received file every time: at position 131016. It doesn't start the repetition at the end of the file, it just interrupts the file at that point and jumps back to the beginning. And it happens that that was the size of the file that was sent before I started using the HasSpaceAvailable event. I can't figure out what the significance of the value 131,016 is. Maximum buffer size somewhere?

Using all kinds of NSLogs and breakpoints, I have pretty much determined that the data is leaving the phone that way, and not being scrambled by the server. I also wrote in an NSMailComposeViewer method that would email me the base64 file as an attachment, and it comes through perfectly.

Here is the code for when the file is read from disk and sent to the server:

    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]];

And then here is the code that uses the HasSpaceAvailable event:

        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;

(I especially like this code because it would allow placing a ProgressView control on the screen.)

The network stream event handler code is in the root view controller, but the image data in base64 is being sent from another view controller. My instinct tells me that this is not a problem because it has worked fine until now, but with much shorter strings.

Now, there's one other issue that may be related -- and probably is. I can't seem to complete the transfer of the data unless I close the app. The server doesn't see it until the connection is closed, I guess. I have tried placing [outputStream close] in various places in the code to no avail. I've also tried terminating the base64 string with a linefeed or carriage return or both.

The server is programmed to save the file when it has seen the correct number of bytes, but that never happens until the app is closed. I know by using WireShark on the server that some of the data is being received, but the remaining data, as I have said, doesn't arrive until the app is closed.

I suspect that this last part (completing the transfer) is the problem, but for the life of me, I can't find anything online that addresses this, unless I am just too ignorant to know what search terms to use.... which is highly likely.

I hope I have given enough information. Can anyone help me?

Was it helpful?

Solution

EDIT

The solution to the problem appeared to be different than what was suspected in the original answer. Through the discussion in the comments the QP has been led to a solution. Here is a summary:

The transmission of data through a raw TCP socket requires thorough handling of the enqueing logic, which was not properly taken into account by the QP. I recommended to use the socket library CocoaAsyncSocket which handles this part of the task and which in turn led the QP to a working solution.

Original Answer:

I'd guess NSString is not up to the task. It's made to hold, well, strings.

Try to read the file into an NSData directly instead and sending it as binary. (Its ascii in the end, isn't it?) Besides, this will be much more resource friendly than your current code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top