2

ドキュメントリストAPIを使用してAndroidアプリをGoogleドキュメント/ドライブとやり取りさせようとしています:https ://developers.google.com/google-apps/documents-list

GData Resumable Upload Protocolを使用してファイルをアップロードする際に少し問題が発生しました。例: 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を「再開可能なメディアの作成」リンクにPOSTすることを通過します。次に、チャンクの送信に使用されるHttpContent実装のwriteTo()メソッドでSSLExceptionがスローされるのを確認します。この例外は、最初のチャンクの途中で発生します。次に例を示します。

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)

上記を確認するには、writeTo()メソッドでIOExceptionを実際にキャプチャする必要があることに注意してください(ログに記録した後でIOExceptionを再スローします)。そうしないと、コンテンツが突然切断されたために、一般的な「400BadRequest」応答が表示されます。

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

提案をありがとう。

更新:コードスニペットを追加します。これは、次のチャンクを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());
    }
}

もう1つの重要な注意事項:SSLExceptionの失敗は、Content-Rangeヘッダーの存在によってトリガーされます。省略した場合、チャンクサイズより大きいファイルの最初のチャンクは例外なく正常にアップロードされます。サーバーは201Createdを返し、ファイルは切り捨てられた512KiBサイズで作成されます。

再度、感謝します!

更新2

これで、問題を実証するために利用できる純粋なJavaアプリができました。これは、ライブラリが含まれているEclipseプロジェクトとしてパッケージ化されていますが、他の場所で使用するのは簡単です。 http://android.nextapp.com/test/DriveUpload.zip

テストするには、認証トークンが必要です。実行するには、次の3つのコマンドラインパラメータが必要です。

[ファイル名][コンテンツ/タイプ][AuthToken]

(現在、実際のアプリのデバッグ出力から認証トークンを切り取って貼り付けています)

このアプリケーションのトリミングされた出力は次のとおりです。

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)
4

1 に答える 1

2

問題は、「Content-Range」ヘッダーの前に「bytes」を付けていなかったことです。仕様との整合性についてヘッダーをチェックした前の半ダース回のいずれかでこれが見つからなかった理由については、私にはわかりません。:)

ご協力いただきありがとうございます。この問題が発生しないことをお詫び申し上げます。

仕様では、PUTリクエストへの応答が、次のアップロード用の「Location」ヘッダーを提供することを示していることに気づきました。表示されていませんが、以前に発行された場所に固執すれば問題なく動作します。私のコードは、ロケーションURLが提供された場合はそれを更新し、提供されなかった場合は以前に発行されたものを続行するようになりました。

于 2012-04-28T09:41:55.757 に答える