2

I'm developing a corporative CRUD application, and one of the features is storing the subscribed user profile picture to Google Cloud, and downloading it when it is needed to display it.

The fact is, when I need to download it, the request comes from the client front end, calling and specific GET route - for example, '/getPic'.

Then my Node back-end server handles this request, communicating with Google Cloud and downloading the file, more or less (simplified) as follows:

let localFile = fs.createWriteStream('temp/writeStreamFile.jpeg');

return new Promise((resolve,reject) =>{

   storage.bucket().file(`users/user123456/profilePicture.jpeg`)
       .createReadStream() 
       .on('end', () => {
           console.log("ended");
           resolve();
       })
       .on('response', ans => {
           console.log("responded");
       })
       .on('error', err => {
           console.error("Error", err);
           reject();
       })
       .pipe(localFile);
})

Afterwards, I return this picture in the response of the HTTP request, and it's fine. The drawback of this operation is that the downloaded file remains stored in my webserver, in the folder ('temp/writeStreamFile.jpeg'), consuming my server storage.

Is there any way to "pipe" the file directly in the response of the HTTP request without saving it locally?

2 Answers2

1

If you would rather not store temporary copies of the image in the server, you could generate and send your users GCS Signed URLs. These URLs give access to storage bucket files for a limited time, and then expire. Since it looks like you are using the Node client for GCS, there is an easy way of generating these URLs, as shown in the documentation.

This is a snippet from the documentation:

const {Storage} = require('@google-cloud/storage');
const storage = new Storage();
const myBucket = storage.bucket('my-bucket');

const file = myBucket.file('my-file');

//-
// Generate a URL that allows temporary access to download your file.
//-
const request = require('request');

const config = {
  action: 'read',
  expires: '03-17-2025',
};

file.getSignedUrl(config, function(err, url) {
  if (err) {
    console.error(err);
    return;
  }

  // The file is now available to read from this URL.
  request(url, function(err, resp) {
    // resp.statusCode = 200
  });
});

Using this method, the user client could download the images directly, there are additional examples in the documentation reference that let you tweak options such as available time for the URL to be used.

ErnestoC
  • 2,660
  • 1
  • 6
  • 19
1

You can actually pipe a ReadableStream directly to your response. A simple implementation using express would look like this:

app.get('/getPic', (req, res) => {
    res.setHeader("content-type", "image/jpeg");
    storage.bucket().file(`users/user123456/profilePicture.jpeg`)
       .createReadStream() 
       .on('end', () => {
           console.log("ended");
       })
       .on('response', ans => {
           console.log("responded");
       })
       .on('error', err => {
           console.error("Error", err);
       })
       .pipe(res);
})
GersonLCSJunior
  • 343
  • 1
  • 8