Как я могу показать прогресс загрузки большого файла из GDrive с помощью API версии 3 на стороне клиента gapi?
Я использую API версии 3 и пытался использовать запрос Range в заголовок, который работает, но загрузка идет очень медленно (ниже). Моя конечная цель - воспроизвести видео 4K. GDrive ограничивает воспроизведение до 1920x1280 . Мой план состоял в том, чтобы загружать фрагменты в IndexedDB через API v3 и воспроизводить данные из локального кэша. У меня это работает, используя код ниже через запросы Range, но это необычайно медленно. Обычная загрузка полного тестового файла в 438 МБ напрямую (например, через веб-страницу GDrive) занимает около 30-35 секунд на моем соединении, и, по совпадению, каждый запрос диапазона 1 МБ занимает почти точно те же 30-35 секунд. Такое чувство, что бэкэнд GDrive читает и отправляет полный файл для каждого поддиапазона?
Я также пытался использовать XHR и fetch для загрузки файла, что не удается. Я использую ссылку webContent (которая обычно заканчивается на &export=download
), но я не могу получить правильные заголовки доступа. Я получаю либо CORS, либо другие странные проблемы с разрешениями. Ссылки webContent отлично работают в тегах <image>
и <video>
sr c. Я ожидаю, что это связано со специальной обработкой разрешений или отсутствующей информацией заголовка, которую браузер обрабатывает специально для этих тегов мультимедиа. Мое решение должно быть в состоянии читать частные (не опубликованные c, не разделяемые) ссылки, следовательно, использовать API v3.
Для видеофайлов, размер которых меньше предела GDrive, я могу установить вверх MediaRecorder и использовать элемент <video>
, чтобы получить данные с прогрессом. К сожалению, ограничение 1920x1080 убивает этот подход для больших файлов, где обратная связь о прогрессе еще более важна.
Это код диапазона Range на стороне клиента, который работает, но необычно медленный для больших (400 МБ - 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. В настоящее время я использую область действия drive.photos.readonly, но у меня возникают те же проблемы, даже если я использую область полного разрешения на запись.
Тангенциально, я не могу получить поток при работе на стороне клиента используя gapi (прекрасно работает в узле на стороне сервера). Это просто странно. Если бы я мог получить поток, я думаю, я мог бы использовать это, чтобы добиться прогресса. Всякий раз, когда я добавляю закомментированную строку для responseType: 'stream'
, я получаю следующую ошибку: The server encountered a temporary error and could not complete your request. Please try again in 30 seconds. That’s all we know.
Конечно, ожидание НЕ помогает, и я могу получить успешный ответ, если я не запрашиваю поток.