Google Drive возобновить загрузку с помощью JavaScript

Я пытаюсь загрузить файлы на Google Диск, используя клиентскую библиотеку API Google для JavaScript и возобновляемый тип загрузки.

Я проверяю подлинность и получаю URI загрузки успешно, но у меня возникли проблемы при отправке реальных данных. Если файл содержит только символы ASCII, файл успешно отправляется на накопитель, но в случае специальных символов (åäö) или двоичного файла (например, PNG) файл будет поврежден. Я думаю, что где-то в процессе файл кодируется в Unicode на стороне клиента.

Если я использую "btoa()" для кодирования необработанных данных в base64 и добавлю заголовок "Content-Encoding: base64" к запросу на отправку данных, файл загрузится нормально. Однако использование этого метода увеличивает накладные расходы на 33%, что довольно много, если планируемый размер загрузки файлов составляет от 100 МБ до 1 ГБ.

Вот несколько примеров кода:

Получение возобновляемого URI загрузки:

// Authentication is already done
var request = gapi.client.request({
    "path": DRIVE_API_PATH, // "/upload/drive/v2/files"
    "method": "POST",
    "params": {
        "uploadType": "resumable"
    },
    "headers": {
        "X-Upload-Content-Type": self.file.type,
        //"X-Upload-Content-Length": self.file.size
        // If this is uncommented, the upload fails because the file size is
        // different (corrupted file). Manually setting to the corrupted file
        // size doesn't give 400 Bad Request.
    },
    "body": {
        // self.file is the file object from <input type="file">
        "title": self.file.name, 
        "mimeType": self.file.type,
        "Content-Lenght": self.file.size,
    }
});

Отправка всего файла за один раз:

// I read the file using FileReader and readAsBinaryString
// body is the reader.result (or btoa(reader.result))
// and this code is ran after the file has been read
var request = gapi.client.request({
    "path": self.resumableUrl, // URI got from previous request
    "method": "PUT",
    "headers": {
        //"Content-Encoding": "base64", // Uploading with base64 works
        "Content-Type": self.file.type
    },
    "body": body
});

Я что-то пропустил? Можно ли загрузить файл в двоичном потоке? Я новичок в загрузке файлов в HTML и Javascript, и я не нашел примеров использования библиотеки Google Javascript с возможностью возобновляемой загрузки. Есть аналогичный вопрос в SO без ответов.

2 ответа

Решение

Типы BLOB-объектов являются горячей темой XMLHttpRequest реализации, и они не являются по-настоящему зрелыми. Я бы порекомендовал вам придерживаться кодировки base64. Клиентская библиотека Google для JavaScript не поддерживает возобновляемую загрузку, поскольку очень маловероятно, что клиентское приложение на стороне браузера загружает очень большие файлы непосредственно в Google Drive.

Что работает

Чтобы загрузить двоичный двоичный объект, используйте gorsub/googleapi cors -upload-sample или мой gist-форк UploaderForGoogleDrive, который захватит access_token из разрыва клиента для вас.

Вот уродливая смесь Promise и кода обратного вызова, которая работает для меня. В качестве предпосылки, gapi,UploaderForGoogleDrive, JSZip должны быть загружены через <script> теги. Фрагмент также пропускает инициализацию гапси и секреты API, которые также необходимы.

function bigCSV(){  // makes a string for a 300k row CSV file
    const rows = new Array(300*1000).fill('').map((v,j)=>{
      return [j,2*j,j*j,Math.random(),Math.random()].join(',');
    });
    return rows.join("\n");
}

function bigZip(){  // makes a ZIP file blob, about 8MB
    const zip = new window.JSZip();
    zip.folder("A").file("big.csv", bigCSV());
    return zip.generateAsync({type:"blob", compression:"DEFLATE"});
    // returns Promise<blob>
}

function upload2(zipcontent){
   'use strict';
    const parent = 'root';
    const spaces = 'drive';
    const metadata = {
      name: 'testUpload2H.zip',
      mimeType: 'application/zip',
      parents: [parent]
    };
    const uploader = new window.UploaderForGoogleDrive({
      file: zipcontent,
      metadata: metadata,
      params: {
        spaces,
        fields: 'id,name,mimeType,md5Checksum,size'
      },
      onProgress: function(x){
         console.log("upload progress:",Math.floor(100*x.loaded/x.total));
      },
      onComplete: function(x){
        if (typeof(x)==='string') x = JSON.parse(x);
        // do something with the file metadata in x
        console.log("upload complete: ");
      },
      onError: function(e){ console.log("upload error: ",e); }
    });
    uploader.upload();
}

function uploadZipFile(){
    'use strict';
    (bigZip()
      .then(upload2)
    );
}

Что не работает

По состоянию на ноябрь 2017 года загрузка двоичного двоичного объекта с gapi.client.request вызов не будет работать из-за проблемы, когда gapi удаляет полезную нагрузку PUT

Я также пытался использовать base64 с gapi, который работает. но хранит файлы base64, а не настоящие двоичные файлы; и API выборки в режиме cors, который работал наполовину, но вызывал ошибки, связанные с CORS, и скрытие ответов, по крайней мере для меня.

Другие вопросы по тегам