1

I am trying to upload an image with it's title to Cloud Storage using react as frontend and busboy for handling the upload in the backend but I can't seem to get it working. When I submit the form I get an error saying that event.target.files[0] is undefined. Any suggestions?

React Code

handleSubmit = (event) => {
        event.preventDefault();

        const image = event.target.files[0];
        const formData = new FormData();
        formData.append('image', image, image.name);
        formData.append('title', this.state.title);
        this.props.addPost(formData)
    };
<form onSubmit={this.handleSubmit}>
     <TextField name="title" type="text" label="Title"placeholder="Add a title"/>
     <input type="file" id="imageInput"/>

     <Button type="submit" variant="contained" color="primary" className={classes.submitButton} disabled={loading}>
            Submit
     </Button>
</form>

and my API function

exports.addPost = (req,res)=> {
    const BusBoy = require('busboy');
    const path = require('path');
    const os = require('os');
    const fs = require('fs');

    const busboy = new BusBoy({ headers: req.headers });

    let imageToBeUploaded = {};
    let imageFileName;

    busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
        console.log(fieldname, file, filename, encoding, mimetype);
        if (mimetype !== 'image/jpeg' && mimetype !== 'image/png') {
          return res.status(400).json({ error: 'Wrong file type submitted' });
        }
        // my.image.png => ['my', 'image', 'png']
        const imageExtension = filename.split('.')[filename.split('.').length - 1];
        // 32756238461724837.png
        imageFileName = `${Math.round(
          Math.random() * 1000000000000
        ).toString()}.${imageExtension}`;
        const filepath = path.join(os.tmpdir(), imageFileName);
        imageToBeUploaded = { filepath, mimetype };
        file.pipe(fs.createWriteStream(filepath));
      });
      busboy.on('finish', () => {
        admin
          .storage()
          .bucket()
          .upload(imageToBeUploaded.filepath, {
            resumable: false,
            metadata: {
              metadata: {
                contentType: imageToBeUploaded.mimetype
              }
            }
          })
          .then(() => {
            const imgUrl = `https://firebasestorage.googleapis.com/v0/b/${
              config.storageBucket
            }/o/${imageFileName}?alt=media`;

            const newPost = {
                imgUrl: imgUrl,
                userHandle: req.user.handle,
                uniName: req.user.uniName,
                title: req.body.title,
                createdAt: new Date().toISOString(),
                likeCount:0,
                commentCount:0
            };
            db.collection('posts').add(newPost).then((doc) => {
                const resPost = newPost;
                resPost.postId = doc.id;
                res.json(resPost);
            })
          })
          .then(() => {
            return res.json({ message: 'image uploaded successfully' });
          })
          .catch((err) => {
            console.error(err);
            return res.status(500).json({ error: 'something went wrong' });
          });
      });
      busboy.end(req.rawBody);   
};
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
sotos bic
  • 89
  • 7
  • Any reason why you're not just using the Cloud Storage web client SDK provided by Firebase? You can bypass the function altogether and upload straight to the bucket. – Doug Stevenson Oct 25 '19 at 12:43
  • I'm only using Firebase Storage temporally just to test the api. After some time I'm gonna use another server for files and firebase for the api and functions – sotos bic Oct 25 '19 at 13:06

1 Answers1

1

You've tied the handleSubmit to the form, but then do this in the code:

handleSubmit = (event) => {
    event.preventDefault();

    const image = event.target.files[0];

Since the handler is tied to the form, event.target refers to the form. And a form doesn't have a files property, hence the error message.

You'll need to look up the input and call files[0] on that.

In React you typically look up a field by defining a ref property in the field:

 <input type="file" id="imageInput" ref="imageInput" />

And then in your code:

const input = this.refs.imageInput;

const image = imageInput.files[0];
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thanks this helped me a lot! The frontend now works perfectly but I still get a response status 500 from server. In Firebase Function Log it says that the field uniName ( that I'm taking from the user ) and title ( passing from the frontend field ) that I'm trying to pass to the new post are undefined. How is that possible since the uniName field exists in user and the title is sent to the api function? – sotos bic Oct 25 '19 at 13:32
  • "When I submit the form I get an error saying that event.target.files[0] is undefined. Any suggestions?" My answer solves this. Please limit your question to a single problem, and not necessarily to a whole use-case. See [how to create a minimal, complete, verifiable example](http://stackoverflow.com/help/mcve). – Frank van Puffelen Oct 25 '19 at 14:00