問題描述
我正在嘗試使用 Android 中的 Drive Rest API 創建可恢復的上傳會話.
I am trying to create a resumable upload session using drive rest API in Android.
根據文檔,需要遵循的 3 個步驟是
As per the documentation the 3 steps needed to be followed are
- 開始一個可恢復的會話
- 保存可恢復的會話 URI
- 上傳文件
第 1 步:我使用以下代碼啟動可恢復會話.
Step 1 : I use the following code to start the resumable session.
File body = new File();
body.setName(fileName);
body.setMimeType(mimeType);
body.setCreatedTime(modifiedDate);
body.setModifiedTime(modifiedDate);
body.setParents(Collections.singletonList(parentId));
HttpHeaders header = new HttpHeaders();
header.setContentLength(0L);
header.setContentType("application/json; charset=UTF-8");
header.set("X-Upload-Content-Type","image/jpeg");
HttpResponse response= driveObject
.files()
.create(body)
.setRequestHeaders(header)
.set("uploadType","resumable")
.buildHttpRequest()
.execute();
第 2 步:執行完成后,我將打印請求的響應標頭以查看位置 URI
Step 2: Once the execution is complete, I'm printing the response header of the request to see the Location URI
System.out.println(response.getHeader().toString());
輸出如下
{
cache-control=[no-cache, no-store, max-age=0, must-revalidate],
content-encoding=[gzip],
content-type=[application/json; charset=UTF-8],
date=[Thu, 06 Oct 2016 02:20:18 GMT],
expires=[Mon, 01 Jan 1990 00:00:00 GMT],
alt-svc=[quic=":443"; ma=2592000; v="36,35,34,33,32"],
pragma=[no-cache],
server=[GSE],
transfer-encoding=[chunked],
vary=[Origin, X-Origin],
x-android-received-millis=[1475720421761],
x-android-response-source=[NETWORK 200],
x-android-sent-millis=[1475720420804],
x-content-type-options=[nosniff],
x-frame-options=[SAMEORIGIN],
x-xss-protection=[1; mode=block]
}
我沒有在響應標頭中找到開始上傳文檔中指定的文件數據的位置 URI,也沒有找到任何 Java 示例來執行可恢復上傳.
I don't find the Location URI in the response header to start uploading filedata as specified in the documentation nor I find any Java samples to perform resumable upload.
如何檢索文檔中指定的位置 URI?
How do I retrieve Location URI as specified in documentation?
推薦答案
我已經嘗試了一周的大部分時間,終于可以運行可恢復的上傳.它不像我預期的那樣工作,但它確實有效.
I was trying for the better part of a week now and I finally got the resumable uploads to run. It does not work how I expected it would, but it does work.
我了解到,據我所知,Google Drive REST API 并不能真正進行分塊上傳.這可能是一個錯誤,也可能是設計使然.我也可能太傻了.
What I learned is that the Google Drive REST API is, as far as I know, not really capable of making chunked uploads. This may be a bug or it may be by design. I may also be too stupid.
但讓我想到的是,我在任何地方都看不到代碼示例.每個人都一直在談論 Http
標頭.所以這就是我們下面要做的.我們將只使用標題.
But what got me thinking was that I could not see code examples anywhere. Everybody just talked about Http
headers all the time. So this is what we're gonna do below. We'll use just the headers.
以下是使用 Google Drive REST API 和 Android 進行可恢復、分塊上傳的方法:
So here is how you do resumable, chunked uploads with the Google Drive REST API and Android:
String accountName = "account_name";
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(context, Arrays.asList(SCOPES)).setBackOff(new ExponentialBackOff()).setSelectedAccountName(accountName);
1) 啟動可恢復會話
遵循 Google 在本文檔
POST /upload/drive/v3/files?uploadType=resumable HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer your_auth_token
Content-Length: 38
Content-Type: application/json; charset=UTF-8
X-Upload-Content-Type: image/jpeg
X-Upload-Content-Length: 2000000
{
"name": "My File"
}
設置所有標題字段,就像在 Google 的示例中一樣.將其作為 POST
請求發送.使用您的 credential
變量來獲取授權令牌.X-Upload-Content-Type
的 mime 類型不是那么重要,沒有它也可以工作(thisSO answer 提供了一個很好的函數來從路徑中檢索它).將 X-Upload-Content-Length
設置為文件的總長度.將 Content-Type
設置為 JSON 格式,因為我們的正文將以 JSON 格式為 Google 提供元數據.
Set all the header fields just like in Google's example. Send it as a POST
request. Use your credential
variable to get the authorization token. The mime type for X-Upload-Content-Type
is not so important, it works without it too (this SO answer provides a nice function to retrieve it from a path). Set the X-Upload-Content-Length
to the total length of your file. Set Content-Type
to JSON format, since our body will provide the metadata for Google in the JSON format.
現在創建您的元數據正文.我輸入了文件名和父級.將 Content-Length
設置為 body
的長度(以字節為單位).然后將你的 body 寫入 request.getOutputStream()
輸出流.
Now create your metadata body. I put in a file name and a parent. Set the Content-Length
to the length of your body
in bytes. Then write your body to the request.getOutputStream()
output stream.
URL url = new URL("https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable");
HttpURLConnection request = (HttpURLConnection) url.openConnection();
request.setRequestMethod("POST");
request.setDoInput(true);
request.setDoOutput(true);
request.setRequestProperty("Authorization", "Bearer " + credential.getToken());
request.setRequestProperty("X-Upload-Content-Type", getMimeType(file.getPath()));
request.setRequestProperty("X-Upload-Content-Length", String.format(Locale.ENGLISH, "%d", file.length()));
request.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
String body = "{"name": "" + file.getName() + "", "parents": ["" + parentId + ""]}";
request.setRequestProperty("Content-Length", String.format(Locale.ENGLISH, "%d", body.getBytes().length));
OutputStream outputStream = request.getOutputStream();
outputStream.write(body.getBytes());
outputStream.close();
request.connect();
2) 保存可恢復會話 URI
最后,connect()
并等待響應.如果響應代碼為 200
,則您已成功啟動分塊可恢復上傳.現在將 location
標頭 URI 保存在某處(數據庫、文本文件等).你以后會需要它的.
2) Save the Resumable Session URI
Finally, connect()
and wait for a response. If the response code is 200
, you have successfully initiated a chunked, resumable upload. Now save the location
header URI somewhere (database, text file, whatever). You're gonna need it later.
if (request.getResponseCode() == HttpURLConnection.HTTP_OK) {
String sessionUri = request.getHeaderField("location");
}
3) 上傳文件
PUT {session_uri} HTTP/1.1
Host: www.googleapis.com
Content-Length: 524288
Content-Type: image/jpeg
Content-Range: bytes 0-524287/2000000
bytes 0-524288
將以下代碼放入循環中,直到上傳整個文件.在每個塊之后,您將收到帶有代碼 308
和 range
標頭的響應.從這個 range
標頭中,您可以讀取下一個塊開始(參見(4)).
Put the following code in a loop, until the entire file is uploaded. After every chunk, you will get a response with code 308
and a range
header. From this range
header, you can read the next chunk start (see (4)).
Content-Type
將再次成為 mime 類型.Content-Length
是您在此塊中上傳的字節數.Content-Range
需要采用 bytes startByte-EndByte/BytesTotal
的形式.你把它放在 PUT
請求中.
Content-Type
is going to be the mime type again. Content-Length
is the number of bytes you upload in this chunk. Content-Range
needs to be of the form bytes startByte-EndByte/BytesTotal
. You put this in a PUT
request.
然后您創建一個 FileInputStream
并將位置設置為您的起始字節(您從上一個響應 range
標頭中獲得)并將另一個塊讀入您的緩沖區.然后將此緩沖區寫入連接輸出流.最后,connect()
.
Then you create a FileInputStream
and set the position to your start byte (which you got from your last response range
header) and read another chunk into your buffer. This buffer is then written to the connection output stream. Finally, connect()
.
URL url = new URL(sessionUri);
HttpURLConnection request = (HttpURLConnection) url.openConnection();
request.setRequestMethod("PUT");
request.setDoOutput(true);
request.setConnectTimeout(10000);
request.setRequestProperty("Content-Type", getMimeType(file.getPath()));
long uploadedBytes = chunkSizeInMb * 1024 * 1024;
if (chunkStart + uploadedBytes > file.length()) {
uploadedBytes = (int) file.length() - chunkStart;
}
request.setRequestProperty("Content-Length", String.format(Locale.ENGLISH, "%d", uploadedBytes));
request.setRequestProperty("Content-Range", "bytes " + chunkStart + "-" + (chunkStart + uploadedBytes - 1) + "/" + file.length());
byte[] buffer = new byte[(int) uploadedBytes];
FileInputStream fileInputStream = new FileInputStream(file);
fileInputStream.getChannel().position(chunkStart);
if (fileInputStream.read(buffer, 0, (int) uploadedBytes) == -1) { /* break, return, exit*/ }
fileInputStream.close();
OutputStream outputStream = request.getOutputStream();
outputStream.write(buffer);
outputStream.close();
request.connect();
4) 處理響應
在此之后,您將收到代碼 308
的響應(如果成功).此響應包含一個 range
標頭(已提及).
4) Handle Response
After this you will get a response with code 308
(if successful). This response contains a range
header (mentioned).
HTTP/1.1 308 Resume Incomplete
Content-Length: 0
Range: bytes=0-524287
您將其拆分并獲取新的塊起始字節.
You split this up and obtain your new chunk start byte.
String range = chunkUploadConnection.getHeaderField("range");
int chunkPosition = Long.parseLong(range.substring(range.lastIndexOf("-") + 1, range.length())) + 1;
5) 響應碼不是308?!
您可能會收到 5xx
響應.您的互聯網連接可能會失敗,文件可能會在上傳過程中被刪除/重命名,等等.別擔心.只要您保存會話 URI 和塊起始字節,您就可以隨時恢復上傳.
5) The Response Code Is Not 308?!
It can happen that you get a 5xx
response. Your internet connection could fail, the file could be deleted/renamed during upload, etc. etc.
Don't worry. As long as you save your session URI and your chunk start byte, you can resume the upload anytime.
為此,請發送以下格式的標頭:
In order to do that, send a header of the following form:
PUT {session_uri} HTTP/1.1
Content-Length: 0
Content-Range: bytes */TotalFileLength
URL url = new URL(sessionUri);
HttpURLConnection request = (HttpURLConnection) url.openConnection();
request.setRequestMethod("PUT");
request.setDoOutput(true);
request.setConnectTimeout(10000);
request.setRequestProperty("Content-Length", "0");
request.setRequestProperty("Content-Range", "bytes */" + file.length());
request.connect();
然后您將收到一個帶有 range
標頭的 308
,您可以從中讀取最后上傳的字節(就像我們在上面所做的那樣).取這個數字并重新開始循環.
You will then receive a 308
with a range
header, from which you can read the last uploaded byte (just as we did above). Take this number and start to loop again.
我希望我能幫助你們中的一些人.如果您還有其他問題,請在評論中提出,我會編輯答案.
I hope I could help some of you. If you have any more questions, just ask in the comments and I will edit the answer.
這篇關于Drive Rest API V3 中的可恢復上傳的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!