Как загрузить файл в Firebase Storage из вызываемой облачной функции https - PullRequest
1 голос
/ 04 ноября 2019

Я пытался загрузить файл в хранилище Firebase , используя функцию облака с возможностью вызова Firebase. Все, что я делаю, - это извлекаю изображение из URL, используя axios, и пытаюсь загрузить его в хранилище. Проблема, с которой я сталкиваюсь, заключается в том, что я не знаю, как сохранить ответ от axios и загрузить его в хранилище.

Во-первых, как сохранить полученный файл во временном каталоге, который создает os.tmpdir(). Тогда как загрузить его в хранилище. Здесь я получаю данные как arraybuffer, а затем преобразую их в Blob и пытаюсь загрузить. Вот мой кодЯ пропустил большую часть, я думаю. Если есть лучший способ, пожалуйста, порекомендуйте меня. Я просмотрел много документации и не нашел четкого решения. Пожалуйста, руководство. Заранее спасибо.


const bucket = admin.storage().bucket();
const path = require('path');
const os = require('os');
const fs = require('fs');
module.exports = functions.https.onCall((data, context) => {
  try {
    return new Promise((resolve, reject) => {
      const {
        imageFiles,
        companyPIN,
        projectId
      } = data;
      const filename = imageFiles[0].replace(/^.*[\\\/]/, '');
      const filePath = `ProjectPlans/${companyPIN}/${projectId}/images/${filename}`; // Path i am trying to upload in FIrebase storage
      const tempFilePath = path.join(os.tmpdir(), filename);
      const metadata = {
        contentType: 'application/image'
      };
      axios
        .get(imageFiles[0], { // URL for the image
          responseType: 'arraybuffer',
          headers: {
            accept: 'application/image'
          }
        })
        .then(response => {
          console.log(response);
          const blobObj = new Blob([response.data], {
            type: 'application/image'
          });
          return blobObj;
        })
        .then(async blobObj => {
          return bucket.upload(blobObj, {
            destination: tempFilePath    // Here i am wrong.. How to set the path of downloaded blob file
          });
        }).then(buffer => {
          resolve({ result: 'success' });
        })
        .catch(ex => {
          console.error(ex);
        });
    });
  } catch (error) {
    // unknown: 500 Internal Server Error
    throw new functions.https.HttpsError('unknown', 'Unknown error occurred. Contact the administrator.');
  }
});

1 Ответ

1 голос
/ 04 ноября 2019

Я бы взял немного другой подход и вообще не использовал бы локальную файловую систему, поскольку она просто tmpfs и будет стоить вам памяти, которую ваша функция использует в любом случае для хранения буфера / большого двоичного объекта, поэтому проще просто избежать ее изапись непосредственно из этого буфера в GCS с использованием метода save для объекта файла GCS.

Вот пример. Я упростил многие ваши настройки и использую функцию http вместо вызываемой. Аналогично, я использую общедоступный образ stackoverflow, а не ваши исходные URL. В любом случае вы сможете использовать этот шаблон, чтобы изменить то, что вам нужно (например, изменить прототип, удалить ответ http и заменить его нужным возвращаемым значением):

const functions = require('firebase-functions');
const axios = require('axios');
const admin = require('firebase-admin');
admin.initializeApp();

exports.doIt = functions.https.onRequest((request, response) => {
    const bucket = admin.storage().bucket();
    const IMAGE_URL = 'https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-logo.svg';
    const MIME_TYPE = 'image/svg+xml';
    return axios.get(IMAGE_URL, { // URL for the image
        responseType: 'arraybuffer',
        headers: {
          accept: MIME_TYPE
        }
      }).then(response => {
        console.log(response);  // only to show we got the data for debugging
        const destinationFile = bucket.file('my-stackoverflow-logo.svg');  
        return destinationFile.save(response.data).then(() => {  // note: defaults to resumable upload
          return destinationFile.setMetadata({ contentType: MIME_TYPE });
        });
      }).then(() => { response.send('ok'); })
      .catch((err) => { console.log(err); })
  });

Каккомментатор отметил, что в приведенном выше примере запрос axios сам делает доступ к внешней сети, и для этого вам потребуется план Blaze или Flame. Тем не менее, это само по себе не похоже на вашу текущую проблему.

Аналогично, это также по умолчанию использует возобновляемую загрузку, которую документация не рекомендует, когда вы делаете большое количествомаленький (<10MB файлов), поскольку есть некоторые издержки. </p>


Вы спросили, как это можно использовать для загрузки нескольких файлов. Вот один из подходов. Во-первых, давайте предположим, что у вас есть функция, которая возвращает обещание, которое загружает один файл по его имени (я сократил это из приведенного выше, но оно в основном идентично, за исключением изменения с INPUT_URL на filename - обратите внимание, что ононе возвращает окончательный результат, такой как response.send(), и существует неявное предположение, что все файлы одинаковы MIME_TYPE):

function downloadOneFile(filename) {
  const bucket = admin.storage().bucket();
  const MIME_TYPE = 'image/svg+xml';
  return axios.get(filename, ...)
    .then(response => {
       const destinationFile = ...
     });
}

Затем вам просто нужно итеративно построить цепочку обещанийиз списка файлов. Допустим, они в imageUrls. После сборки верните всю цепочку:

let finalPromise = Promise.resolve();
imageUrls.forEach((item) => { finalPromise = finalPromise.then(() => downloadOneFile(item)); });

// if needed, add a final .then() section for the actual function result

return finalPromise.catch((err) => { console.log(err) });

Обратите внимание, что вы также можете создать массив обещаний и передать их в Promise.all() - это, вероятно, будет быстрее, если вы получите некоторый параллелизм, ноЯ бы не советовал, если вы не уверены, что все данные поместятся в память вашей функции одновременно. Даже при таком подходе необходимо убедиться, что все загрузки могут быть завершены в течение времени ожидания вашей функции.

...