Sulman Azhar
Sulman Azhar

Reputation: 1079

Upload image to cloud storage from firebase cloud functions

I am trying so hard to upload one image from cloud functions I am sending an image from the web to the cloud function using onRequest. I am sending a base64 string and the fileName. Now I was following different tutorials on the internet and couldn't seem to solve my problem.

Here is my code. I think I am doing something wrong with the service account json. Although i generated the json file and used it but still it didn't work.

I get the error of The caller does not have permission at Gaxios._request when i don't use service account json And when i do use serviceAccount.json then i get this error The "path" argument must be of type string. Received an instance of Object which is from file.createWriteStream() i think

Anyway here is the code can anyone please help me with this

The projectId that I am using is shown in the picture below

const functions = require("firebase-functions");
const admin = require("firebase-admin");

const projectId = functions.config().apikeys.projectid; // In the picture below

const stream = require("stream");

const cors = require("cors")({ origin: true });

const { Storage } = require("@google-cloud/storage");
//  Enable Storage
const storage = new Storage({
  projectId: projectId, // I did use serviceAccount json here but that wasn't working
});




// With serviceAccount.json code
// const storage = new Storage({
//      projectId: projectId,
//      keyFilename: serviceAccount,
//    });
// This is giving the error of: The "path" argument must be of type string. Received an instance of Object
    
exports.storeUserProfileImage = functions.https.onRequest((req, res) => {
  cors(req, res, async () => {
    try {
      const bucket = storage.bucket(`gs://${projectId}.appspot.com`);

      let pictureURL;
      const image = req.body.image;
      const userId = req.body.userId;
      const fileName = req.body.fileName;

      const mimeType = image.match(
        /data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,.*/
      )[1];
      //trim off the part of the payload that is not part of the base64 string
      const base64EncodedImageString = image.replace(
        /^data:image\/\w+;base64,/,
        ""
      );
      const imageBuffer = Buffer.from(base64EncodedImageString, "base64");
      const bufferStream = new stream.PassThrough();
      bufferStream.end(imageBuffer);
      // Define file and fileName
      const file = bucket.file("images/" + fileName);

      bufferStream
        .pipe(
          file.createWriteStream({
            metadata: {
              contentType: mimeType,
            },
            public: true,
            validation: "md5",
          })
        )
        .on("error", function (err) {
          console.log("error from image upload", err.message);
        })
        .on("finish", function () {
          // The file upload is complete.
          console.log("Image uploaded");
          file
            .getSignedUrl({
              action: "read",
              expires: "03-09-2491",
            })
            .then((signedUrls) => {
              // signedUrls[0] contains the file's public URL
              console.log("Signed urls", signedUrls[0]);
              pictureURL = signedUrls[0];
            });
        });
      console.log("image url", pictureURL);
      res.status(200).send(pictureURL);
    } catch (e) {
      console.log(e);
      return { success: false, error: e };
    }
  });
});

enter image description here

Upvotes: 3

Views: 745

Answers (2)

Sulman Azhar
Sulman Azhar

Reputation: 1079

So first of all I didn't give any serviceaccounts because I am using the firebase cloud functions as @Dharmaraj said in his answer Secondly, this was a permission problem in the google cloud platform which can be solved by going through the following steps

Go to your project's Cloud Console (https://console.cloud.google.com/) > IAM & admin > IAM, Find the App Engine default service account then click on the pencil at far left > Click on add role > In the filter field enter Service Account Token Creator and click on it save and you are good to go

Found this solution from here https://github.com/firebase/functions-samples/issues/782

Upvotes: 1

Dharmaraj
Dharmaraj

Reputation: 50830

const storage = new Storage({
  projectId: projectId
  keyFilename: "" // <-- Path to a .json, .pem, or .p12 key file
});

keyFilename accepts path to where your service account is stored and the credentials themselves.

folder
  |-index.js
  |-credentials
    |-serviceAccountKey.json

If your directory structure looks like about then the path should be like this:

const storage = new Storage({
  projectId: projectId
  keyFilename: "./credentials/serviceAccountKey.json"
});

Do note that if you are using Cloud functions then the SDK will use Application Default Credentials so you don't have to pass those params. Simply initialize as shown below:

const storage = new Storage()

Upvotes: 2

Related Questions