2013-02-18 84 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

相關問題