2012-04-27 131 views
2

我試圖得到一個Android應用使用文檔列表API與谷歌文檔/驅動互動:https://developers.google.com/google-apps/documents-list文檔列表API文件上傳失敗時,文件大小比繼續上傳塊大小更大

我在跑使用GData可恢復上傳協議上傳文件有點麻煩,即: https://developers.google.com/google-apps/documents-list#uploading_a_new_document_or_file_with_both_metadata_and_content

我正在使用512KiB塊大小。小於塊大小的文件成功上傳,但大於塊大小的文件將在第一塊完成之前失敗。即使將塊大小增加到1MiB或2MiB,情況也是如此。一個768KiB文件將失敗,塊大小爲512KiB,但成功的塊大小爲1MiB。

大於塊大小的文件確實超過了第一步,即將XML張貼到「可恢復的創建媒體」鏈接。然後我看到用於發送塊的HttpContent實現的writeTo()方法中拋出的SSLException。這通過異常FIRST塊中途發生,例如:

javax.net.ssl.SSLException: Write error: ssl=0x2a6318: I/O error during system call, Broken pipe 
    at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_write(Native Method) 
    at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLOutputStream.write(OpenSSLSocketImpl.java:713) 
    at libcore.net.http.FixedLengthOutputStream.write(FixedLengthOutputStream.java:41) 
    at com.google.api.client.http.AbstractInputStreamContent.writeTo(AbstractInputStreamContent.java:89) 

注意看上面的,我不得不實際拍攝的IOExceptions在我的writeTo()方法(登錄後他們他們重新拋出)。否則,由於內容被突然中斷,我只會看到一個通用的「400錯誤請求」響應。我已經嘗試了幾種不同的測試設備,例如Galaxy Nexus(VZW/CDMA,4.0.2,股票),Droid4(Moto 2.3.6,股票)DroidX(股票)。

頁眉上的要求上傳第一個塊:

Accept-Encoding: gzip 
Authorization: GoogleLogin auth=XXXXXXXXXXXXXXXXXXXXXXXXXXXXX 
Content-Length: 524288 
Content-Range: 0-524287/1635085 
Content-Type: image/png 
GData-Version: 3.0 
User-Agent: XXXXXXXXXXXXXX 

感謝您的任何建議。

UPDATE:添加代碼片段,這是PUT下一個塊的位。

private boolean putNext() 
throws CancelException, DirectoryException { 
    try { 
     ByteArrayContent content = new ByteArrayContent(mediaType, buffer, 0, bufferLength); 

     HttpRequest request = conn.getHttpRequestFactory().buildPutRequest(new GenericUrl(nextLocation), content); 

     HttpHeaders requestHeaders = request.getHeaders(); 
     requestHeaders.setContentLength(String.valueOf(bufferLength)); 
     requestHeaders.setContentType(mediaType); 

     if (sent != 0 || bufferLength != size) { 
      // Only set content range when file will span multiple chunks. 
      requestHeaders.setContentRange(sent + "-" + (sent + bufferLength - 1) + "/" + size); 
     } 

     HttpResponse response = request.execute(); 

     sent += bufferLength; 
     bufferLength = 0; 

     HttpHeaders responseHeaders = response.getHeaders(); 

     int statusCode = response.getStatusCode(); 
     if (statusCode == GoogleDriveConnection.RESPONSE_308_RESUME_INCOMPLETE) { 
      nextLocation = responseHeaders.getLocation(); 
      return false; 
     } else if (statusCode >= 200 && statusCode < 400) { 
      Document responseDom = DomUtil.load(response.getContent()); 
      return true; 
     } else { 
      Log.w(LOG_TAG, "Google Drive upload error: Invalid response code to upload request: " + statusCode); 
      throw DirectoryException.networkErrorHost(null, catalog.getHostName()); 
     } 
    } catch (IOException ex) { 
     Log.w(LOG_TAG, "Google Drive write error.", ex); 
     throw DirectoryException.networkErrorHost(ex, catalog.getHostName()); 
    } catch (SAXException ex) { 
     throw DirectoryException.networkErrorHost(ex, catalog.getHostName()); 
    } 
} 

另一個可能很重要的注意事項:SSLException失敗是由存在Content-Range標頭觸發的。如果我忽略它,那麼第一塊大於塊大小的文件將成功上傳,沒有例外。服務器返回201 Created,並創建截斷512KiB大小的文件。

再次感謝!

更新2

我現在有一個可用的純Java應用程序來證明這個問題。這被打包成一個包含庫的Eclipse項目,但應該在其他地方使用。 http://android.nextapp.com/test/DriveUpload.zip

驗證令牌需要測試它。它需要三個命令行參數來運行:

[文件名] [內容/類型] [的authToken]

(我目前正在削減從調試輸出在真正的應用程序/粘貼authtokens)

這裏的此應用程序的修整輸出:

TOKEN=******** 
FILE=/tmp/1199Panigale.jpg 
Headers: POST Request 
-- User-Agent: DriveUpload 
-- GData-Version: 3.0 
-- Authorization: GoogleLogin auth=******** 
-- X-Upload-Content-Type: image/png 
-- X-Upload-Content-Length: 1635085 
[POST Request] -------------------------------------------------------- 
<entry xmlns="http://www.w3.org/2005/Atom"> 
<title>1199Panigale.jpg</title> 
</entry> 
Executing post request: https://docs.google.com/feeds/upload/create-session/default/private/full/folder%3Aroot/contents?convert=false 
Post complete, response=200 
Headers: POST Response 
-- Server: HTTP Upload Server Built on Apr 23 2012 11:11:29 (1335204689) 
-- Location: https://docs.google.com/feeds/upload/create-session/default/private/full/folder%3Aroot/contents?convert=false&upload_id=********2 
-- Date: Sat, 28 Apr 2012 04:51:47 GMT 
-- Pragma: no-cache 
-- Expires: Fri, 01 Jan 1990 00:00:00 GMT 
-- Cache-Control: no-cache, no-store, must-revalidate 
-- Content-Length: 0 
-- Content-Type: text/html 
Preparing PUT request #0 
Headers: Put Request 
-- User-Agent: DriveUpload 
-- GData-Version: 3.0 
-- Authorization: GoogleLogin auth=******** 
-- Content-Type: image/png 
-- Content-Range: 0-524287/1635085 
[writeTo] 
[getCotent] 
Read: 4096, total: 4096 
Read: 4096, total: 8192 
Read: 4096, total: 12288 
Read: 4096, total: 16384 

[---skip a few---] 

Read: 4096, total: 270336 
Read: 4096, total: 274432 
Read: 4096, total: 278528 
Read: 4096, total: 282624 
Read: 4096, total: 286720 
Read: 4096, total: 290816 
Apr 27, 2012 9:49:02 PM org.apache.http.impl.client.DefaultRequestDirector tryExecute 
INFO: I/O exception (java.net.SocketException) caught when processing request: Broken pipe 
Apr 27, 2012 9:49:02 PM org.apache.http.impl.client.DefaultRequestDirector tryExecute 
INFO: Retrying request 
[writeTo] 
[getCotent] 
Read: 4096, total: 4096 
Read: 4096, total: 8192 
Read: 4096, total: 12288 
Read: 4096, total: 16384 
Read: 4096, total: 20480 

[---skip a few---] 

Read: 4096, total: 274432 
Read: 4096, total: 278528 
Read: 4096, total: 282624 
Read: 4096, total: 286720 
Read: 4096, total: 290816 
Apr 27, 2012 9:49:02 PM org.apache.http.impl.client.DefaultRequestDirector tryExecute 
INFO: I/O exception (java.net.SocketException) caught when processing request: Broken pipe 
Apr 27, 2012 9:49:02 PM org.apache.http.impl.client.DefaultRequestDirector tryExecute 
INFO: Retrying request 
[writeTo] 
[getCotent] 
Read: 4096, total: 4096 
Read: 4096, total: 8192 
Read: 4096, total: 12288 
Read: 4096, total: 16384 
Read: 4096, total: 20480 

[---skip a few---] 

Read: 4096, total: 278528 
Read: 4096, total: 282624 
Read: 4096, total: 286720 
Read: 4096, total: 290816 
Read: 4096, total: 294912 
Apr 27, 2012 9:49:02 PM org.apache.http.impl.client.DefaultRequestDirector tryExecute 
INFO: I/O exception (java.net.SocketException) caught when processing request: Broken pipe 
Apr 27, 2012 9:49:02 PM org.apache.http.impl.client.DefaultRequestDirector tryExecute 
INFO: Retrying request 
[writeTo] 
[getCotent] 
Read: 4096, total: 4096 
Read: 4096, total: 8192 
Read: 4096, total: 12288 
Read: 4096, total: 16384 
Read: 4096, total: 20480 

[---skip a few---] 

Read: 4096, total: 278528 
Read: 4096, total: 282624 
Read: 4096, total: 286720 
java.net.SocketException: Broken pipe 
    at java.net.SocketOutputStream.socketWrite0(Native Method) 
    at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109) 
    at java.net.SocketOutputStream.write(SocketOutputStream.java:153) 
    at sun.security.ssl.OutputRecord.writeBuffer(OutputRecord.java:314) 
    at sun.security.ssl.OutputRecord.write(OutputRecord.java:303) 
    at sun.security.ssl.SSLSocketImpl.writeRecordInternal(SSLSocketImpl.java:768) 
    at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:756) 
    at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:108) 
    at org.apache.http.impl.io.AbstractSessionOutputBuffer.write(AbstractSessionOutputBuffer.java:153) 
    at org.apache.http.impl.io.ContentLengthOutputStream.write(ContentLengthOutputStream.java:114) 
    at driveupload.UploadStream$1.writeTo(UploadStream.java:149) 
    at org.apache.http.entity.HttpEntityWrapper.writeTo(HttpEntityWrapper.java:96) 
    at org.apache.http.impl.client.EntityEnclosingRequestWrapper$EntityWrapper.writeTo(EntityEnclosingRequestWrapper.java:108) 
    at org.apache.http.impl.entity.EntitySerializer.serialize(EntitySerializer.java:120) 
    at org.apache.http.impl.AbstractHttpClientConnection.sendRequestEntity(AbstractHttpClientConnection.java:264) 
    at org.apache.http.impl.conn.AbstractClientConnAdapter.sendRequestEntity(AbstractClientConnAdapter.java:224) 
    at org.apache.http.protocol.HttpRequestExecutor.doSendRequest(HttpRequestExecutor.java:255) 
    at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:123) 
    at org.apache.http.impl.client.DefaultRequestDirector.tryExecute(DefaultRequestDirector.java:647) 
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:464) 
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:820) 
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:754) 
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:732) 
    at driveupload.DriveUpload.executeRequest(DriveUpload.java:71) 
    at driveupload.UploadStream.putNext(UploadStream.java:191) 
    at driveupload.UploadStream.write(UploadStream.java:225) 
    at java.io.OutputStream.write(OutputStream.java:116) 
    at driveupload.DriveUpload.test(DriveUpload.java:127) 
    at driveupload.DriveUpload.main(DriveUpload.java:44) 
+0

您可以與我們分享您的代碼中可恢復上傳的部分嗎?另外,看看這個[回答](http://stackoverflow.com/a/10195674/1106381),它顯示瞭如何使用gdata-java-client庫1.4.7版插入(或更新)一個新文件使用文檔列表API。 – Alain 2012-04-27 15:49:12

+0

@Alain,謝謝,添加了一段代碼和一些更多的信息,請參閱編輯問題。我實際上並沒有在其網站上使用gdata-java-client per語句,它不再被主動維護。 – tliebeck 2012-04-27 21:08:27

+0

感謝您的代碼片段,我會看看,並試圖看看我能否找出問題來自哪裏。此外,您是否可以確保在啓動[可恢復上傳請求](http://goo.gl/HrMNk)時指定了「X-Upload-Content-Length」標題?最後,您可以查看[ResumableHttpUloadTask.upload](http://code.google.com/p/gdata-java-client/source/browse/trunk/java/src/com/google/gdata/client /uploader/ResumableHttpUploadTask.java#191)方法來查看它是如何在我們的庫中實現的(它仍然有一些發展:))。 – Alain 2012-04-27 21:37:15

回答

2

的問題原來是,我是無法前綴「字節」的「內容範圍」標頭。我不知道爲什麼我在前六次查找標題時都沒有發現這一點,因爲它與規範保持一致。 :)

感謝您的幫助,並抱歉打擾您與這個非問題。

雖然我注意到規範指出對PUT請求的響應將爲下一次上載提供「位置」標頭。我沒有看到,但如果我堅持以前發佈的位置,它工作正常。我的代碼現在只需更新位置URL(如果提供了該位置URL),並繼續以前發佈的位置URL(如果沒有)。