Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resumable uploads (Google Drive SDK for Android or Java)

I had referred to Check progress for Upload & Download, Resumable upload using Drive API for Android and Direct and Resumable Media Uploads.

However, I cannot get solution from them, and the resumable media upload is always failed.

My test step is try to disconnect WiFi/connect to another WiFi during upload, and then upload the same file again, but it always upload the file from beginning.

Any suggestion is appreciated.

Logcat:

08-13 21:56:02.541 13190 13968 D UploadFileActivity: [MEDIA_IN_PROGRESS] progress: 19%
08-13 21:56:02.931 13190 13968 D UploadFileActivity: [MEDIA_IN_PROGRESS] progress: 19%
08-13 21:56:03.262 13190 13968 W System.err: javax.net.ssl.SSLException: Read error: ssl=0x7ac0ba00: I/O error during system call, Connection timed out
08-13 21:56:03.262 13190 13968 W System.err:    at com.android.org.conscrypt.NativeCrypto.SSL_read(Native Method)
08-13 21:56:03.262 13190 13968 W System.err:    at com.android.org.conscrypt.OpenSSLSocketImpl$SSLInputStream.read(OpenSSLSocketImpl.java:692)
08-13 21:56:03.262 13190 13968 W System.err:    at java.io.BufferedInputStream.fillbuf(BufferedInputStream.java:168)
08-13 21:56:03.262 13190 13968 W System.err:    at java.io.BufferedInputStream.read(BufferedInputStream.java:227)
08-13 21:56:03.262 13190 13968 W System.err:    at com.android.okhttp.internal.Util.readAsciiLine(Util.java:316)
08-13 21:56:03.262 13190 13968 W System.err:    at com.android.okhttp.internal.http.RawHeaders.fromBytes(RawHeaders.java:308)
08-13 21:56:03.272 13190 13968 W System.err:    at com.android.okhttp.internal.http.HttpTransport.readResponseHeaders(HttpTransport.java:135)
08-13 21:56:03.272 13190 13968 W System.err:    at com.android.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:644)
08-13 21:56:03.272 13190 13968 W System.err:    at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:347)
08-13 21:56:03.272 13190 13968 W System.err:    at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:296)
08-13 21:56:03.272 13190 13968 W System.err:    at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:503)
08-13 21:56:03.272 13190 13968 W System.err:    at com.android.okhttp.internal.http.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:136)
08-13 21:56:03.272 13190 13968 W System.err:    at com.google.api.client.http.javanet.NetHttpResponse.<init>(NetHttpResponse.java:36)
08-13 21:56:03.272 13190 13968 W System.err:    at com.google.api.client.http.javanet.NetHttpRequest.execute(NetHttpRequest.java:94)
08-13 21:56:03.272 13190 13968 W System.err:    at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:965)
08-13 21:56:03.272 13190 13968 W System.err:    at com.google.api.client.googleapis.media.MediaHttpUploader.executeCurrentRequestWithoutGZip(MediaHttpUploader.java:545)
08-13 21:56:03.272 13190 13968 W System.err:    at com.google.api.client.googleapis.media.MediaHttpUploader.resumableUpload(MediaHttpUploader.java:417)
08-13 21:56:03.272 13190 13968 W System.err:    at com.google.api.client.googleapis.media.MediaHttpUploader.upload(MediaHttpUploader.java:336)
08-13 21:56:03.272 13190 13968 W System.err:    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:418)
08-13 21:56:03.272 13190 13968 W System.err:    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:343)
08-13 21:56:03.272 13190 13968 W System.err:    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:460)
08-13 21:56:03.272 13190 13968 W System.err:    at com.example.drivequickstart.UploadFileActivity$UploadFileTask.doInBackground(UploadFileActivity.java:94)
08-13 21:56:03.272 13190 13968 W System.err:    at com.example.drivequickstart.ServiceBaseActivity$GeneralDriveTask.doInBackground(ServiceBaseActivity.java:1)
08-13 21:56:03.272 13190 13968 W System.err:    at android.os.AsyncTask$2.call(AsyncTask.java:288)
08-13 21:56:03.272 13190 13968 W System.err:    at java.util.concurrent.FutureTask.run(FutureTask.java:237)
08-13 21:56:03.272 13190 13968 W System.err:    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
08-13 21:56:03.272 13190 13968 W System.err:    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
08-13 21:56:03.272 13190 13968 W System.err:    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
08-13 21:56:03.272 13190 13968 W System.err:    at java.lang.Thread.run(Thread.java:864)
08-13 21:56:12.792 13190 13190 D UploadFileActivity:  onConnected()
08-13 21:56:12.812 13190 14305 D UploadFileActivity: [INITIATION_STARTED]
08-13 21:56:13.432 13190 14305 D UploadFileActivity: [INITIATION_COMPLETE]
08-13 21:56:13.953 13190 14305 D UploadFileActivity: [MEDIA_IN_PROGRESS] progress: 0%
08-13 21:56:14.203 13190 14305 D UploadFileActivity: [MEDIA_IN_PROGRESS] progress: 0%
08-13 21:56:14.514 13190 14305 D UploadFileActivity: [MEDIA_IN_PROGRESS] progress: 0%
08-13 21:56:14.754 13190 14305 D UploadFileActivity: [MEDIA_IN_PROGRESS] progress: 0%
08-13 21:56:15.044 13190 14305 D UploadFileActivity: [MEDIA_IN_PROGRESS] progress: 1%
08-13 21:56:15.294 13190 14305 D UploadFileActivity: [MEDIA_IN_PROGRESS] progress: 1%

Code snip:

private class UploadFileTask extends GeneralDriveTask {
    @Override
    protected Boolean doInBackground(String... params) {
        try {
            java.io.File fileContent = new java.io.File(fileUri.getPath());

            // File's metadata.
            File fileMetadata = new File();
            fileMetadata.setTitle(fileContent.getName());
            //body.setMimeType("image/jpeg");
            fileMetadata.setMimeType("video/mp4");
            fileMetadata.setFileSize(fileContent.length());

            // File's binary content

            InputStreamContent mediaContent = new InputStreamContent("video/mp4", new BufferedInputStream(new FileInputStream(fileContent)));
            mediaContent.setLength(fileContent.length());

            Drive.Files.Insert insert = getDriveService().files().insert(fileMetadata, mediaContent).set("uploadType", "resumable");
            insert.set("uploadType", "resumable");
            insert.getMediaHttpUploader().setDirectUploadEnabled(false);
            insert.getMediaHttpUploader().setChunkSize(MediaHttpUploader.MINIMUM_CHUNK_SIZE);
            insert.getMediaHttpUploader().setProgressListener(mUploadFileProgressListener);

            File file = insert.execute();   
            if (file != null)
                showToast("Video uploaded: " + file.getTitle());                
        } catch (UserRecoverableAuthIOException e) {
            GetGoogleDriveToken();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return true;
    }   
}

private static class UploadFileProgressListener implements MediaHttpUploaderProgressListener {
    @Override
    public void progressChanged(MediaHttpUploader uploader) throws IOException {
        switch (uploader.getUploadState()) {
            case INITIATION_STARTED:
            Log.d(LOG_TAG, "[INITIATION_STARTED]");
              break;
            case INITIATION_COMPLETE:
                Log.d(LOG_TAG, "[INITIATION_COMPLETE]");
              break;

            case MEDIA_IN_PROGRESS:
                int percent = (int)(uploader.getProgress() * 100);
                Log.d(LOG_TAG, "[MEDIA_IN_PROGRESS] progress: " + percent + "%");
                break;
            case MEDIA_COMPLETE:
                Log.d(LOG_TAG, "[MEDIA_COMPLETE] progress: 100%");
                break;
            default:
                break;              
        }
    }
};
like image 572
Caxton Avatar asked Oct 01 '22 02:10

Caxton


1 Answers

It seems that MediaHttpUploader did not handle resume upload well. The reason is that "Request the upload status." mentioned in Google Drive SDK Upload Files always fail to get Content-Range.

So, The way to solve it is polling Content-Range all the time during upload. Thus it's able to resuming an interrupted upload by "Resume the upload from the point where it left off."

private String getUploadID(Uri fileUri, String token) {         
    Log.d(LOG_TAG, "[sendResumableHttpRequest] +++");

    String upload_id = "";

    java.io.File fileContent = new java.io.File(fileUri.getPath());
    String fileName = fileContent.getName();
    String contentLength = String.valueOf(fileContent.length());
    String mimeType = getMimeFromURI(fileUri);

    Log.d(LOG_TAG, "[sendResumableHttpRequest] fileName : " + fileName);
    Log.d(LOG_TAG, "[sendResumableHttpRequest] contentLength : " + contentLength);
    Log.d(LOG_TAG, "[sendResumableHttpRequest] mimeType : " + mimeType);

    try {
        String url = "https://www.googleapis.com/upload/drive/v2/files?uploadType=resumable";
        URL obj = new URL(url);
        HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();

        //add reuqest header
        con.setRequestMethod("POST");
        con.setRequestProperty("Authorization", "Bearer " + token);
        con.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
        con.setRequestProperty("X-Upload-Content-Type", mimeType);
        con.setRequestProperty("X-Upload-Content-Length", contentLength);       

        JSONObject jobj = new JSONObject();
        jobj.put("title", fileName);
        byte[] postData = jobj.toString().getBytes();           

        // Send post request
        con.setDoOutput(true);
        DataOutputStream wr = new DataOutputStream(con.getOutputStream());
        wr.write(postData);
        wr.flush();
        wr.close();

        int responseCode = con.getResponseCode();
        String location = con.getHeaderField("Location");
        if (location.contains("upload_id")) {
            String[] uploadParameters = location.split("upload_id");
            upload_id = uploadParameters[1].replace("=", "");
        }

        Log.d(LOG_TAG, "[sendResumableHttpRequest] Response Code : " + responseCode);
        Log.d(LOG_TAG, "[sendResumableHttpRequest] Response Location : " + location);
        Log.d(LOG_TAG, "[sendResumableHttpRequest] Response uploadID : " + upload_id);

        BufferedReader in = new BufferedReader(
                new InputStreamReader(con.getInputStream()));
        String inputLine;
        StringBuffer response = new StringBuffer();

        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }
        in.close();
    } catch (Exception e) {
        e.printStackTrace();            
    }               

    Log.d(LOG_TAG, "[sendResumableHttpRequest] ---");

    return upload_id;
}

private void putFileWithUploadID(Uri fileUri, String token, long range) {       
    Log.d(LOG_TAG, "[putFileWithUploadID] +++");

    java.io.File fileContent = new java.io.File(fileUri.getPath());
    String fileName = fileContent.getName();
    String contentLength = String.valueOf(fileContent.length());
    String mimeType = getMimeFromURI(fileUri);

    Log.d(LOG_TAG, "[putFileWithUploadID] fileName : " + fileName);
    Log.d(LOG_TAG, "[putFileWithUploadID] contentLength : " + contentLength);
    Log.d(LOG_TAG, "[putFileWithUploadID] mimeType : " + mimeType);

    long totalBytesFromDataInputStream = 0;

    try {
        String url = "https://www.googleapis.com/upload/drive/v2/files?uploadType=resumable&upload_id=" + mUploadID;
        URL obj = new URL(url);
        HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();

        //add reuqest header
        con.setRequestMethod("PUT");
        con.setRequestProperty("Authorization", "Bearer " + token);
        con.setRequestProperty("Content-Type", mimeType);
        //con.setFixedLengthStreamingMode(MediaHttpUploader.MINIMUM_CHUNK_SIZE);

        long nextByte = 0;
        if (range == 0) {               
            con.setRequestProperty("Content-Length", contentLength);
        }
        else {
            nextByte = range + 1;
            long remainContentLength = Long.parseLong(contentLength) - nextByte;
            con.setRequestProperty("Content-Length", String.valueOf(remainContentLength));
            String contentRange = "bytes " + (range + 1) + "-" + ( Long.parseLong(contentLength) - 1) + "/" + contentLength;
            con.setRequestProperty("Content-Range", contentRange);

            Log.d(LOG_TAG, "[putFileWithUploadID] Content-Length : " + String.valueOf(remainContentLength));
            Log.d(LOG_TAG, "[putFileWithUploadID] Content-Range : " + contentRange);
        }
        con.setDoOutput(true);          

        DataOutputStream wr = new DataOutputStream(con.getOutputStream());

        @SuppressWarnings("resource")
        DataInputStream inputStream = new DataInputStream(new FileInputStream(fileContent)); 

        int bytes = 0;              
        byte[] bufferOut = new byte[1024];
        while ((bytes = inputStream.read(bufferOut)) != -1) {               
            if (nextByte > 0) {
                nextByte = nextByte - bytes;
                if (nextByte >= 0) continue;
            }
            wr.write(bufferOut, 0, bytes);  
            totalBytesFromDataInputStream += bytes;
        }                                  
        Log.d(LOG_TAG, "[putFileWithUploadID] totalBytesFromDataInputStream:" + totalBytesFromDataInputStream);
        wr.flush();
        wr.close();

        int responseCode = con.getResponseCode();

        Log.d(LOG_TAG, "[putFileWithUploadID] Response Code : " + responseCode);

        BufferedReader in = new BufferedReader(
                new InputStreamReader(con.getInputStream()));
        String inputLine;
        StringBuffer response = new StringBuffer();

        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }
        in.close();

        isUploadCompleted = true;
        isInterrupted = false;
        mLastUploadBytes = 0;
        Log.d(LOG_TAG, "[putFileWithUploadID] isUploadCompleted:" + isUploadCompleted);
        Log.d(LOG_TAG, "[putFileWithUploadID] isInterrupted:" + isInterrupted);
    } catch (Exception e) {
        e.printStackTrace();            
        isInterrupted = true;
        Log.d(LOG_TAG, "[putFileWithUploadID] isInterrupted:" + isInterrupted);
        Log.d(LOG_TAG, "[putFileWithUploadID] totalBytesFromDataInputStream:" + totalBytesFromDataInputStream);
    } finally {
    }

    Log.d(LOG_TAG, "[putFileWithUploadID] ---");
}

private void putFileWithUploadID(Uri fileUri, String token) {   
    putFileWithUploadID(fileUri, token, 0);
}   

private long requestUploadStatus(Uri fileUri, String token) {           
    Log.d(LOG_TAG, "[requestUploadStatus] +++");

    String range_so_far = "0";

    java.io.File fileContent = new java.io.File(fileUri.getPath());
    String contentLength = String.valueOf(fileContent.length());

    Log.d(LOG_TAG, "[requestUploadStatus] contentLength : " + contentLength);

    try {
        String url = "https://www.googleapis.com/upload/drive/v2/files?uploadType=resumable&upload_id=" + mUploadID;
        URL obj = new URL(url);
        HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();

        //add reuqest header
        con.setRequestMethod("PUT");
        con.setRequestProperty("Authorization", "Bearer " + token);
        con.setRequestProperty("Content-Length", "0");
        con.setRequestProperty("Content-Range", "bytes */*");           

        // Send post request
        con.setDoOutput(true);
        DataOutputStream wr = new DataOutputStream(con.getOutputStream());
        wr.flush();
        wr.close();

        int responseCode = con.getResponseCode();
        String rangeHeader = con.getHeaderField("Range");
        if (rangeHeader != null && rangeHeader.length() > 0) {
            String[] range = rangeHeader.split("-");
            range_so_far = range[1];
        }

        Log.d(LOG_TAG, "[requestUploadStatus] Response Code : " + responseCode);
        Log.d(LOG_TAG, "[requestUploadStatus] Response rangeHeader : " + rangeHeader);
        Log.d(LOG_TAG, "[requestUploadStatus] Response range_so_far : " + range_so_far);                        

        BufferedReader in = new BufferedReader(
                new InputStreamReader(con.getInputStream()));
        String inputLine;
        StringBuffer response = new StringBuffer();

        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }

        in.close();
    } catch (Exception e) {
        e.printStackTrace();            
    }               

    Log.d(LOG_TAG, "[requestUploadStatus] ---");

    return Long.parseLong(range_so_far);
}   
like image 199
Caxton Avatar answered Nov 03 '22 19:11

Caxton