Reputation: 1453
What's the proper way to return stream from Firebase callable function? I want it to behave like a proxy and return the response stream directly. Currently, I wait for response and append them like shown below. I don't think it's the right way to do it as it won't return stream directly.
const axios = require('axios');
const functions = require("firebase-functions");
const {PassThrough} = require("stream");
exports.create = functions.https.onCall(async(data, context) => {
const options = {
method: 'POST',
url: '...someurl',
,
responseType: 'stream'
};
const response = await axios(options)
const chunks = response.data.pipe(new PassThrough({encoding:'base64'}));
// then we use an async generator to read the chunks
let str = '';
for await (let chunk of chunks) {
str += chunk;
}
return str;
}
Upvotes: 3
Views: 1636
Reputation: 1453
If you don't want to upload data to a bucket and directly return it as base64, I found this way is better instead of concanetating chunks yourself. In this way we use promise and then to make sure function waits for the end of the promise.
const axios = require('axios');
const functions = require("firebase-functions");
const {PassThrough} = require("stream");
exports.create = functions.https.onCall(async(data, context) => {
let promise = new Promise((resolve, reject) => {
inputData.pipe(concat({encoding: 'buffer'}, buf => {
const options = {
method: 'POST',
url: '....',
data: buf,
responseType: "arraybuffer"
};
axios.request(options).then(function (response) {
let base64String = response.data.toString('base64')
resolve(base64String)
}).catch(function (error) {
reject(error)
});
}))
})
return promise.then(
function(value) {
console.log('succeed')
return value;
},
function(error) {
console.log(error)
}
);
}
Upvotes: 0
Reputation: 2023
If you have access to cloud storage buckets you can upload the data to a bucket, generate a download URL and redirect your HTTP request to the download link
//using admin sdk and express
import * as admin from "firebase-admin";
const bucket = admin.storage().bucket('<bucket name>');
await bucket.file('<filename>').save(data).catch();
bucket.file('<filename>').getSignedURL({
action: 'read',
expires: Date.now() + 600000, //expires in 10 minutes
}).then(url => {
response.redirect(url[0]); //redirect to download URL
}).catch(error => {
console.log(error);
})
You will have to change your storage permissions settings to authenticate the download URL creation and access. You can do this in the rules
section of the storage project category on firebase
service firebase.storage {
match /b/{bucket}/o {
match /<bucket name>/{allPaths=**} {
allow read: if true;
allow write: if request.auth != null; //all other locations are secured by auth
}
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
Ensure you replace <bucket name>
with the name of your bucket. This rule allows anyone to read from the databucket but not write to it unless authorized. All other locations are secure. If you are storing confidential information do not use this permissions workaround and secure the download URL creation by correct auth.
Upvotes: 2