Thomas David Kehoe
Thomas David Kehoe

Reputation: 10930

Firebase Storage: what is `byteLength`? How to set MIME types of file uploads?

When I upload a .mp3 audio file or a .jpg picture to Firebase Cloud Storage I get this error:

TypeError: Cannot read properties of undefined (reading 'byteLength')

What is byteLength?

I tried uploading a .jpg with raw uploadString. The file appeared in Storage but it was only 15B when it should have been 100KB.

The documentation Upload files with Cloud Storage on Web doesn't say anything about specifying MIME types when uploading files.

Writing to the Storage emulator executes without errors but no files appear.

Here's the Firebase Cloud Function I'm using.

import { initializeApp } from "firebase/app";
import * as functions from "firebase-functions";
import { getStorage, ref, uploadBytes, uploadString, connectStorageEmulator } from "firebase/storage";

const firebaseConfig = {
    apiKey: "...",
    authDomain: "my-awesome-app.firebaseapp.com",
    databaseURL: "https://my-awesome-app.firebaseio.com",
    projectId: "my-awesome-app",
    storageBucket: "my-awesome-app.appspot.com",
    messagingSenderId: "...",
    appId: "..."
};

const app = initializeApp(firebaseConfig);

export const ByteMe = functions.firestore.document('ByteMe/{userID}').onUpdate((change, context) => {
    const storage = getStorage();
    const storageRef = ref(storage, 'gs://my-awesome-app.appspot.com/Pictures/bootchkas.jpg');
    connectStorageEmulator(storage, "localhost", 9199); // comment out to write to the cloud

    async function uploadPicture() {
        try {
            uploadString(storageRef, './bootchkas.jpg').then((snapshot) => {
                console.log('Uploaded a raw string!');
            });
        } catch (error) {
            console.error(error);
        }
    }
    
    return uploadPicture();
});

Upvotes: 1

Views: 539

Answers (2)

Thomas David Kehoe
Thomas David Kehoe

Reputation: 10930

As @Dharmaraj and @Alexander N. said, uploadString just uploads the subsequent string, not a file. The error message

TypeError: Cannot read properties of undefined (reading '...')

means that undefined is a missing ES module that you didn't import.

The following code almost uploads a file...with one problem.

import file from "./bootchkas.jpg";

export const ByteMe = functions.firestore.document('ByteMe/{userID}').onUpdate((change, context) => {
    const storage = getStorage();
    const storageRef = ref(storage, 'gs://my-awesome-app.appspot.com/Pictures/bootchkas.jpg'); // location to write to
    connectStorageEmulator(storage, "localhost", 9199); // comment out to write to the cloud

    const metadata = {
        contentType: 'image/jpeg',
    };

    async function uploadPicture() {
        try {
            await uploadBytes(storageRef, file, metadata);
            console.log('Uploaded a file!');
        } catch (error) {
            console.error(error);
        }
    }

    return uploadPicture();
});

uploadBytes uploads files (or blobs). It take three parameters: the storageRef or path to a location in Storage; file, which is the file to be uploaded to Storage; and metadata, which includes the MIME type of the file.

I've set the MIME contentType to 'image/jpeg'.

This leaves file. We need to import the file as an ES module:

import file = './bootchkas.jpg`;

But that doesn't work because bootchkas.jpg isn't an ES module.

We need to use WebPack to make an ES module from bootchkas.jpg. The WebPack documentation has a section Loading Images. I followed the tutorial and was able to load bootchkas.com into a HTML page. But when I tried to use WebPack with my Firebase Cloud Function it threw error after error.

Here's the webpack.config.js I ended up with (bizarrely, the tutorial use CommonJS modules):

import path from "path"

const __dirname = "/Users/TDK/VisualStudioCodeProjects/CloudFunctions/functions"

const exportMe = {
    mode: 'development',
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist'),
    },
    module: {
        rules: [
            {
                test: /\.(png|svg|jpg|jpeg|gif)$/i,
                type: 'asset/resource',
            },
        ],
    },
};

export default exportMe;

This throws about a half-dozen error, which I fixed one by one.

Then I restarted the Firebase emulator and it couldn't read functions/package.json. WebPack requires changing "main": "./src/index.js", to "private": true, which makes Firebase unable to reach your Cloud Functions. Trying to get WebPack to work with Firebase Cloud Functions is too difficult.

Upvotes: 0

Alexander N.
Alexander N.

Reputation: 1592

As @Dharmaraj indicated, it looks like you are just uploading the string ./bootchkas.jpg -> its exactly 15 bytes long. Is this the code you wanted to use to upload the file? If it is, it won't find the file, but only find the string ./bookchkas.jpg.

If you want to upload a file as a string you would either need to encode the file as base64 or load the file itself and send that to firebase storage. If using the browser APIs, you can fetch the file using something akin to this. If using nodejs and a server side application, you will want to refer to the file using the filesystem nodejs package.

Upvotes: 1

Related Questions