我需要帮助编写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类没有插入()方法