javascript - 如何从可调用的 https 云函数将文件上传到 Firebase 存储
问题描述
我一直在尝试使用可调用的 Firebase 云功能将文件上传到Firebase 存储。我正在做的就是使用axios
并尝试上传到存储从 URL 获取图像。我面临的问题是,我不知道如何保存来自 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.');
}
});
解决方案
我会采取一种稍微不同的方法并完全避免使用本地文件系统,因为它只是 tmpfs 并且会花费你的内存,你的函数无论如何都要使用它来保存缓冲区/blob,所以更简单地避免它并直接从使用GCS 文件对象上的save
方法将该缓冲区缓存到 GCS 。
这是一个例子。我已经简化了很多设置,并且我使用的是 http 函数而不是可调用函数。同样,我使用的是公共 stackoverflow 图片,而不是您的原始网址。无论如何,您应该能够使用此模板修改回您需要的内容(例如更改原型并删除 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 文件)时,文档不建议这样做,因为会有一些开销。
您问如何使用它来下载多个文件。这是一种方法。首先,假设您有一个函数返回一个promise,该promise 会根据文件名下载单个文件(我已经从上面删减了这一点,但除了更改为INPUT_URL
to之外,它基本相同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 数组并将它们传递给Promise.all()
- 这可能会更快,因为您可以获得一些并行性,但我不建议您这样做,除非您非常确定所有数据都适合内存你的功能一次。即使使用这种方法,您也需要确保下载可以在您的函数超时内全部完成。
推荐阅读
- c# - API 控制器在 AWS lambda 上托管后不返回文件
- sql - 根据时间合并两个表
- python - 错误类型错误:无法读取未定义的属性“_id”
- vb.net - 从存储过程中的第二个选择填充数据表(VB.Net)
- android - 如何在 Android 项目中通过 Dagger 2 提供 Activity 上下文?
- amazon-web-services - 如何在不允许公共访问的情况下允许存储桶中的文档访问同一存储桶中的其他文档?
- tensorflow - TensorFlow 会话图为空。在调用 run() 之前向图中添加操作
- swift - 当我的应用程序在前台 Swift iOS 上时,是否可以检测到何时显示第 3 方推送通知
- node.js - 运行在 Windows 上为我的树莓派创建的节点 Web 应用程序时出错
- python-3.x - Tkinter 使按钮比预期的要大