GDrive API v3 files.get 下载进度?
GDrive API v3 files.get download progress?
如何使用 gapi client-side v3 API 显示从 GDrive 下载大文件的进度?
我正在使用 v3 API,我尝试在 header 中使用 Range 请求,这有效,但下载速度非常慢(如下)。我的最终目标是播放 4K 视频。 GDrive limits playback to 1920x1280。我的计划是通过 v3 API 下载块到 IndexedDB 并从本地缓存的数据播放。我通过范围请求使用下面的代码进行了此工作,但速度慢得无法使用。直接(例如通过 GDrive 网页)正常下载完整的 438 MB 测试文件在我的连接上大约需要 30-35 秒,而且巧合的是,每个 1 MB Range 请求几乎完全相同的 30-35 秒。感觉 GDrive back-end 正在读取和发送每个子范围的完整文件?
我也尝试过使用 XHR 和 fetch 来下载文件,但失败了。我一直在使用 webContent link(通常以 &export=download
结尾)但我无法正确访问 header。我遇到 CORS 或其他奇怪的权限问题。 webContent links 在 <image>
和 <video>
src 标签中工作正常。我希望这是由于特殊权限处理或某些 header 信息,我缺少浏览器专门为这些媒体标签处理的信息。我的解决方案必须能够读取 private (non-public, non-sharable) links,因此使用 v3 API.
对于小于 GDrive 限制的视频文件,我可以设置一个 MediaRecorder 并使用 <video>
元素来获取进度数据。不幸的是,1920x1080 的限制扼杀了这种处理较大文件的方法,其中进度反馈更为重要。
这是 client-side gapi Range 代码,它可以工作,但对于大型 (400 MB - 2 GB) 文件来说速度慢得无法使用:
const getRange = (start, end, size, fileId, onProgress) => (
new Promise((resolve, reject) => gapi.client.drive.files.get(
{ fileId, alt: 'media', Range: `bytes=${start}-${end}` },
// { responseType: 'stream' }, Perhaps this fails in the browser?
).then(res => {
if (onProgress) {
const cancel = onProgress({ loaded: end, size, fileId })
if (cancel) {
reject(new Error(`Progress canceled download at range ${start} to ${end} in ${fileId}`))
}
}
return resolve(res.body)
}, err => reject(err)))
)
export const downloadFileId = async (fileId, size, onProgress) => {
const batch = 1024 * 1024
try {
const chunks = []
for (let start = 0; start < size; start += batch) {
const end = Math.min(size, start + batch - 1)
const data = await getRange(start, end, size, fileId, onProgress)
if (!data) throw new Error(`Unable to get range ${start} to ${end} in ${fileId}`)
chunks.push(data)
}
return chunks.join('')
} catch (err) {
return console.error(`Error downloading file: ${err.message}`)
}
}
身份验证对我来说工作正常,我使用其他 GDrive 命令也很好。我目前正在使用 drives.photos.readonly 范围,但即使我使用完整的 write-permission 范围,我也会遇到同样的问题。
切线地,当 运行 client-side 使用 gapi 时,我无法获得流(在 server-side 上的节点中工作正常)。这很奇怪。如果我能得到一个流,我想我可以用它来取得进步。每当我为 responseType: 'stream'
添加 commented-out 行时,我都会收到以下错误: The server encountered a temporary error and could not complete your request. Please try again in 30 seconds. That’s all we know.
当然等待没有帮助,如果我不请求流,我可以获得成功的响应.
我转而直接使用 XMLHttpRequest,而不是 gapi 包装器。 Google provides these instructions for using CORS 展示了如何将任何请求从使用 gapi 转换为 XHR。然后您可以附加到 onprogress 事件(以及 onload、onerror 和其他事件)以获取进度。
这是问题中 downloadFileId 方法的直接替换代码,带有一堆调试脚手架:
const xhrDownloadFileId = (fileId, onProgress) => new Promise((resolve, reject) => {
const user = gapi.auth2.getAuthInstance().currentUser.get()
const oauthToken = user.getAuthResponse().access_token
const xhr = new XMLHttpRequest()
xhr.open('GET', `https://www.googleapis.com/drive/v3/files/${fileId}?alt=media`)
xhr.setRequestHeader('Authorization', `Bearer ${oauthToken}`)
xhr.responseType = 'blob'
xhr.onloadstart = event => {
console.log(`xhr ${fileId}: on load start`)
const { loaded, total } = event
onProgress({ loaded, size: total })
}
xhr.onprogress = event => {
console.log(`xhr ${fileId}: loaded ${event.loaded} of ${event.total} ${event.lengthComputable ? '' : 'non-'}computable`)
const { loaded, total } = event
onProgress({ loaded, size: total })
}
xhr.onabort = event => {
console.warn(`xhr ${fileId}: download aborted at ${event.loaded} of ${event.total}`)
reject(new Error('Download aborted'))
}
xhr.onerror = event => {
console.error(`xhr ${fileId}: download error at ${event.loaded} of ${event.total}`)
reject(new Error('Error downloading file'))
}
xhr.onload = event => {
console.log(`xhr ${fileId}: download of ${event.total} succeeded`)
const { loaded, total } = event
onProgress({ loaded, size: total })
resolve(xhr.response)
}
xhr.onloadend = event => console.log(`xhr ${fileId}: download of ${event.total} completed`)
xhr.ontimeout = event => {
console.warn(`xhr ${fileId}: download timeout after ${event.loaded} of ${event.total}`)
reject(new Error('Timout downloading file'))
}
xhr.send()
})
如何使用 gapi client-side v3 API 显示从 GDrive 下载大文件的进度?
我正在使用 v3 API,我尝试在 header 中使用 Range 请求,这有效,但下载速度非常慢(如下)。我的最终目标是播放 4K 视频。 GDrive limits playback to 1920x1280。我的计划是通过 v3 API 下载块到 IndexedDB 并从本地缓存的数据播放。我通过范围请求使用下面的代码进行了此工作,但速度慢得无法使用。直接(例如通过 GDrive 网页)正常下载完整的 438 MB 测试文件在我的连接上大约需要 30-35 秒,而且巧合的是,每个 1 MB Range 请求几乎完全相同的 30-35 秒。感觉 GDrive back-end 正在读取和发送每个子范围的完整文件?
我也尝试过使用 XHR 和 fetch 来下载文件,但失败了。我一直在使用 webContent link(通常以 &export=download
结尾)但我无法正确访问 header。我遇到 CORS 或其他奇怪的权限问题。 webContent links 在 <image>
和 <video>
src 标签中工作正常。我希望这是由于特殊权限处理或某些 header 信息,我缺少浏览器专门为这些媒体标签处理的信息。我的解决方案必须能够读取 private (non-public, non-sharable) links,因此使用 v3 API.
对于小于 GDrive 限制的视频文件,我可以设置一个 MediaRecorder 并使用 <video>
元素来获取进度数据。不幸的是,1920x1080 的限制扼杀了这种处理较大文件的方法,其中进度反馈更为重要。
这是 client-side gapi Range 代码,它可以工作,但对于大型 (400 MB - 2 GB) 文件来说速度慢得无法使用:
const getRange = (start, end, size, fileId, onProgress) => (
new Promise((resolve, reject) => gapi.client.drive.files.get(
{ fileId, alt: 'media', Range: `bytes=${start}-${end}` },
// { responseType: 'stream' }, Perhaps this fails in the browser?
).then(res => {
if (onProgress) {
const cancel = onProgress({ loaded: end, size, fileId })
if (cancel) {
reject(new Error(`Progress canceled download at range ${start} to ${end} in ${fileId}`))
}
}
return resolve(res.body)
}, err => reject(err)))
)
export const downloadFileId = async (fileId, size, onProgress) => {
const batch = 1024 * 1024
try {
const chunks = []
for (let start = 0; start < size; start += batch) {
const end = Math.min(size, start + batch - 1)
const data = await getRange(start, end, size, fileId, onProgress)
if (!data) throw new Error(`Unable to get range ${start} to ${end} in ${fileId}`)
chunks.push(data)
}
return chunks.join('')
} catch (err) {
return console.error(`Error downloading file: ${err.message}`)
}
}
身份验证对我来说工作正常,我使用其他 GDrive 命令也很好。我目前正在使用 drives.photos.readonly 范围,但即使我使用完整的 write-permission 范围,我也会遇到同样的问题。
切线地,当 运行 client-side 使用 gapi 时,我无法获得流(在 server-side 上的节点中工作正常)。这很奇怪。如果我能得到一个流,我想我可以用它来取得进步。每当我为 responseType: 'stream'
添加 commented-out 行时,我都会收到以下错误: The server encountered a temporary error and could not complete your request. Please try again in 30 seconds. That’s all we know.
当然等待没有帮助,如果我不请求流,我可以获得成功的响应.
我转而直接使用 XMLHttpRequest,而不是 gapi 包装器。 Google provides these instructions for using CORS 展示了如何将任何请求从使用 gapi 转换为 XHR。然后您可以附加到 onprogress 事件(以及 onload、onerror 和其他事件)以获取进度。
这是问题中 downloadFileId 方法的直接替换代码,带有一堆调试脚手架:
const xhrDownloadFileId = (fileId, onProgress) => new Promise((resolve, reject) => {
const user = gapi.auth2.getAuthInstance().currentUser.get()
const oauthToken = user.getAuthResponse().access_token
const xhr = new XMLHttpRequest()
xhr.open('GET', `https://www.googleapis.com/drive/v3/files/${fileId}?alt=media`)
xhr.setRequestHeader('Authorization', `Bearer ${oauthToken}`)
xhr.responseType = 'blob'
xhr.onloadstart = event => {
console.log(`xhr ${fileId}: on load start`)
const { loaded, total } = event
onProgress({ loaded, size: total })
}
xhr.onprogress = event => {
console.log(`xhr ${fileId}: loaded ${event.loaded} of ${event.total} ${event.lengthComputable ? '' : 'non-'}computable`)
const { loaded, total } = event
onProgress({ loaded, size: total })
}
xhr.onabort = event => {
console.warn(`xhr ${fileId}: download aborted at ${event.loaded} of ${event.total}`)
reject(new Error('Download aborted'))
}
xhr.onerror = event => {
console.error(`xhr ${fileId}: download error at ${event.loaded} of ${event.total}`)
reject(new Error('Error downloading file'))
}
xhr.onload = event => {
console.log(`xhr ${fileId}: download of ${event.total} succeeded`)
const { loaded, total } = event
onProgress({ loaded, size: total })
resolve(xhr.response)
}
xhr.onloadend = event => console.log(`xhr ${fileId}: download of ${event.total} completed`)
xhr.ontimeout = event => {
console.warn(`xhr ${fileId}: download timeout after ${event.loaded} of ${event.total}`)
reject(new Error('Timout downloading file'))
}
xhr.send()
})