6
votes

Please help

I receive images from the client and save it on my server in the file system and process this image, after which I need to upload it to firebase storage

I try upload image file to firebase storage from Node.js in my async function

const path = process.cwd() + '/my_image.jpg';
const file = readFileSync(path);
await firebase.storage().ref().child('my_image.jpg').put(file);
...

But i have error

The first argument must be of type string or an instance of Buffer. Received an instance of Uint8Array

Okey, i try binary format

const path = process.cwd() + '/my_image.jpg';
const file = readFileSync(path, { encoding: 'base64' });
await firebase.storage().ref().child('my_image.jpg').putString(file, 'base64');
...

And i get error

Firebase Storage: String does not match format 'base64': Invalid character found"

I've already tried a lot of things, but nothing works! What am I doing wrong?

4

4 Answers

15
votes

You can use this code right here

var admin = require("firebase-admin");
const uuid = require('uuid-v4');

// CHANGE: The path to your service account
var serviceAccount = require("path/to/serviceAccountKey.json");

admin.initializeApp({
    credential: admin.credential.cert(serviceAccount),
    storageBucket: "<BUCKET_NAME>.appspot.com"
});

var bucket = admin.storage().bucket();

var filename = "path/to/image.png"

async function uploadFile() {

  const metadata = {
    metadata: {
      // This line is very important. It's to create a download token.
      firebaseStorageDownloadTokens: uuid()
    },
    contentType: 'image/png',
    cacheControl: 'public, max-age=31536000',
  };

  // Uploads a local file to the bucket
  await bucket.upload(filename, {
    // Support for HTTP requests made with `Accept-Encoding: gzip`
    gzip: true,
    metadata: metadata,
  });

console.log(`${filename} uploaded.`);

}

uploadFile().catch(console.error);

To successfully run this code, you will need to:

  • Add the Firebase Admin SDK to Your Server
  • Install uuid-v4
  • Replace "path/to/serviceAccountKey.json" with the path to your own service account. Here is a guide to get yours.
  • Replace <BUCKET_NAME> with the name of your default bucket. You can find this name in the Storage section of your Firebase Console. The bucket name must not contain gs:// or any other protocol prefixes. For example, if the bucket URL displayed in the Firebase Console is gs://bucket-name.appspot.com, pass the string bucket-name.appspot.com to the Admin SDK.
  • Replace "path/to/image.png" with the path to your own image.
  • If needed, adjust the contentType in the metadata accordingly.

Just to let you know, whenever you upload an image using Firebase Console, an access token will be automatically generated. However, if you upload an image using any Admin SDK or gsutil you will need to manually generate this access token yourself. That's why it is very important the uuid part

Firebase Support says that this is being fixed, but I think anyone having this problem should go this way instead of waiting for Firebase to fix this.

0
votes

For Node js there is a library called '@google-cloud/storage

const {Storage} = require('@google-cloud/storage');

// Creates a client
const storage = new Storage();

const bucket = storage.bucket("my-bucket.appspot.com");
await bucket.upload(
            '/my_image_path.jpg', 
            {
                destination: 'my_uploaded_image.jpg',
                metadata: {
                    cacheControl: "public, max-age=315360000", 
                    contentType: "image/jpeg"
             }
        });

https://www.npmjs.com/package/@google-cloud/storage

You probably need to authenticate your nodejs client with service account key. See this https://cloud.google.com/docs/authentication/getting-started

0
votes

Maybe your node version does not support readFileSync function with { encoding: 'base64' } option.

Try original way to convert a buffer to a string:

const file = readFileSync(path).toString('base64');
// now file is a base64 string
await firebase.storage().ref().child('my_image.jpg').putString(file, 'base64');
0
votes

Maybe uploading a Uint8Array to Storage was not available a few months ago, but now you can do it. The only thing is you have to add the content type in a metadata object, this way:

const file = new Uint8Array(...)
const metadata = { contentType: 'image/jpeg; charset=utf-8' }
const storageRef = firebase.storage().ref().child('path/to/image.jpg')

await storageRef.put(file, metadata).then((snapshot) => {
  console.log('Uploaded an array!', snapshot)
})