Java Google Drive API V3 MultiPart & Resumable Upload



我需要帮助编写multipart&可恢复的大文件上传(>5MB)到目前为止,我只能开始多次上传,但我不知道如何在用户暂停或网络故障时恢复。

我所说的"恢复"是指我不知道如何

1) 获取已上载到驱动器的总字节数

2) 如何在内容范围标题中使用该值

3) 如何通过用户交互暂停上传[executeAsInputStream()也许?]

这就是我迄今为止所做的。我需要代码从它停止上传的地方恢复,即使我要强制停止应用程序并重新启动

Drive service = GDrive.getService(); //Drive Specific Initialization Copied From QuickStart But with DriveScopes.FILES
File fileMetadata = new File();
fileMetadata.setName("Video.mp4"); //Video File
fileMetadata.setMimeType("application/vnd.google-apps.video");
java.io.File filePath = new java.io.File("E:\large-file-60MB.mp4");//Large File Of 60 Mega Bytes
FileContent mediaContent = new FileContent("video/mp4",filePath);
Drive.Files.Create create=service.files().create(fileMetadata,mediaContent);
MediaHttpUploader uploader=create.getMediaHttpUploader();
uploader.setDirectUploadEnabled(false);                       //Use Resumable MultiPart Upload Protocol
uploader.setChunkSize(2*MediaHttpUploader.MINIMUM_CHUNK_SIZE); //Chunks Of Bytes To Upload With Each Request
// HttpHeaders headers=new HttpHeaders();
// headers.put("Content-Range",?);          //This is not actual code which I used here but after reading the drive docs they talk about this header and I am not sure how or when to use it
// uploader.setInitiationHeaders(headers);
uploader.setProgressListener((uploading)->
{
switch (uploading.getUploadState())
{
case INITIATION_STARTED:System.out.println("Initiation has started!");
break;
case INITIATION_COMPLETE:System.out.println("Initiation is complete!");
break;
case MEDIA_IN_PROGRESS:
System.out.println("Progress="+uploading.getProgress());
System.out.println("Bytes="+uploading.getNumBytesUploaded());
break;
case MEDIA_COMPLETE:System.out.println("Upload is complete!");
}
});
create.execute(); 

虽然在一个答案中回答多个问题通常不适合Stack Overflow,但这些问题似乎都紧密相连,因此将概述可恢复上传,并在这样做的过程中尝试解决您的三点:

  • 如何获取已上传到驱动器的总字节数
  • 如何使用内容范围标题中的值
  • 如何暂停可恢复上传

来自Google关于在其他Java API客户端库上直接和可恢复媒体上传的文档:

实现详细信息

感兴趣的主要类是MediaHttpUploader和MediaHttpProgressListener。

如果特定于服务的生成库中的方法在Discovery文档中包含mediaUpload参数,则会为这些方法创建一个方便的方法,该方法将InputStreamContent作为参数。

例如,Drive API的insert方法支持mediaUpload,您可以使用以下代码上传文件:

class CustomProgressListener implements MediaHttpUploaderProgressListener {
public void progressChanged(MediaHttpUploader uploader) throws IOException {
switch (uploader.getUploadState()) {
case INITIATION_STARTED:
System.out.println("Initiation has started!");
break;
case INITIATION_COMPLETE:
System.out.println("Initiation is complete!");
break;
case MEDIA_IN_PROGRESS:
System.out.println(uploader.getProgress());
break;
case MEDIA_COMPLETE:
System.out.println("Upload is complete!");
}
}
}
File mediaFile = new File("/tmp/driveFile.jpg");
InputStreamContent mediaContent =
new InputStreamContent("image/jpeg",
new BufferedInputStream(new FileInputStream(mediaFile)));
mediaContent.setLength(mediaFile.length());
Drive.Files.Insert request = drive.files().insert(fileMetadata, mediaContent);
request.getMediaHttpUploader().setProgressListener(new CustomProgressListener());
request.execute();

然而,这些类抽象掉了创建可恢复上传时返回的位置URI等内容,因此,如果您希望能够做到这一点,则需要遵循此处所述的可恢复上传启动步骤。然而,这一切都是手动完成的,而不是直接使用Google Drive API客户端库。

要回答第一点,如何存储已上传的字节数取决于您。而不是思考"驱动器上已经有多少">,思考"我已经上传了多少">

如果您愿意,您可以将其存储为本地可用的,因为它将是块大小的倍数(在您的情况下为2 * MediaHttpUploader.MINIMUM_CHUNK_SIZE),并且应该易于跟踪。

问题是,这实际上并不需要。根据文档(重点是我自己的),您可以使用通配符来指示文件的当前位置未知:

如果上传请求在响应之前终止,或者如果您收到503服务不可用响应,则需要恢复中断的上传。

要请求上传状态,请创建一个对可恢复会话URI的空PUT请求。

添加Content-Range标头以指示文件中的当前位置未知。例如,如果文件总长度为2000000字节,请将Content-Range设置为*/2000000如果您不知道文件的完整大小,请将Content-Range设置为*/*

如果您确实想跟踪字节,可以在Content-Range标头中将其指定为

Content-Range: bytes_so_far/total_bytes

步骤:

要初始化可恢复上载,您需要向驱动器API的/upload端点发出POST请求。为此,您不需要使用Drive API客户端库(实际上,如果您想获得可恢复的会话URI,则不能,因为客户端库不提供此信息)。

假设您的凭证定义来自:

GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(...);

然后发出包含文件元数据的POST请求:

URL requestUrl = new URL("https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable");
String requestBody = "{"name": "fileName"}";
HttpURLConnection request = (HttpURLConnection) requestUrl.openConnection();
request.setRequestMethod("POST");
request.setDoInput(true);
request.setDoOutput(true);
request.setRequestProperty("Authorization", "Bearer " + credential.getToken());
request.setRequestProperty("X-Upload-Content-Type", "file/mimetype");
request.setRequestProperty("X-Upload-Content-Length", number_of_bytes_of_your_file);
request.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
request.setRequestProperty("Content-Length", String.format(Locale.ENGLISH, "%d", requestBody.getBytes().length));
OutputStream outputStream = request.getOutputStream();
outputStream.write(requestBody.getBytes());
outputStream.close();
request.connect();

会话URI(在需要时调用以恢复)在API响应的头部中返回。连接后,您可以从响应中获得此URI:

if (request.getResponseCode() == HttpURLConnection.HTTP_OK) {
URL sessionUri = new URL(request.getHeaderField("location"));
}

现在你有了会话URI——有了这个,你可以随心所欲地将文件块上传到Drive。您现在需要使用这个URI作为连续上传的上传点。

不过请记住:可恢复的会话URI将在一周后过期

如何暂停可恢复的上载:

这实际上取决于你希望如何实现这一点。例如,您可以打破一个循环,或者在GUI中有一个巨大的PAUSE THIS UPLOAD按钮,用于切换上传的下一部分是否继续。

需要记住的是,在上传文件内容时,必须使用HTTP PUT而不是POST来完成请求。继上一节之后:

// set these variables:
long beginningOfChunk = 0;
long chunkSize = 2 * MediaHttpUploader.MINIMUM_CHUNK_SIZE;
int chunksUploaded = 0;
// Here starts the upload chunk code:
HttpURLConnection request = (HttpURLConnection) sessionUri.openConnection();
request.setRequestMethod("PUT");
request.setDoOutput(true);
// change your timeout as you desire here:
request.setConnectTimeout(30000); 
request.setRequestProperty("Content-Type", "file/mimetype");
long bytesUploadedSoFar = chunksUploaded * chunkSize;
if (beginningOfChunk + chunkSize > number_of_bytes_of_your_file) {
chunkSize = (int) number_of_bytes_of_your_file - beginningOfChunk;
}
request.setRequestProperty("Content-Length", String.format(Locale.ENGLISH, "%d", chunkSize));
request.setRequestProperty("Content-Range", "bytes " + beginningOfChunk + "-" + (beginningOfChunk + chunkSize - 1) + "/" + number_of_bytes_of_your_file);
byte[] buffer = new byte[(int) chunksize];
FileInputStream fileInputStream = new FileInputStream(yourFile);
fileInputStream.getChannel().position(beginningOfChunk);
fileInputStream.close();
OutputStream outputStream = request.getOutputStream();
outputStream.write(buffer);
outputStream.close();
request.connect();
chunksUploaded += 1;
// End of upload chunk section

然后,您可以重复调用上传区块代码;在循环中,作为一个函数;随心所欲。由于它是一个独特的代码块,您可以随意调用它,因此可以实现某种暂停上传的方式(通过休息、睡眠、等待等)。

请记住:您需要保存会话URI才能继续。


更新:

直接使用Drive V3 API进行可恢复的上传似乎还不可能。Java客户端库文档在讨论何时使用Drive: create与非服务特定库时提到了这一点:

。。。驱动器API的插入方法支持mediaUpload,可以使用以下代码上载文件:

代码块

PD_9

功能请求:

但是,您可以让Google知道,这是一个对Drive API直接很重要的功能,而不是使用非服务特定库的要求。谷歌的Issue Tracker是开发者报告问题并为其开发服务提出功能请求的地方。此处是为驱动器API提交功能请求的页面。

差异说明:驱动器API V2中称为Drive.Files.Create的内容在驱动器API V3中更改为CCD_23。

参考文献:

  • Java Quickstart | Google Drive API | Google开发者
  • Drive.Files.Create(驱动器API v3(修订版197)1.25.0)
  • 创建和填充文件夹| Google Drive API | Google开发者
  • 上传文件数据| Google Drive API | Google开发者
  • 文件:创建| Google Drive API | Google开发者
  • 直接和可恢复的媒体上载|用于Java的API客户端库

相关问题:

  • Google Drive api Java-我的大型多部分上传完成了吗
  • 在Google Drive Java API中设置uploadType
  • 在驱动器休息API V3中可恢复上载
  • Google Drive API-Drive.Files类没有插入()方法

最新更新