Search code examples
node.jsgoogle-cloud-storagemulter

Uploading images to google cloud storage with multer and nodejs getting error


== updated question on 9/9 ===

Tried to use Multer directly without the Middleware like before and using Postman to upload the images.

From Nodejs, req return

  files: [Object: null prototype] {
    imagebackup: [ [Object] ],
    imagebanner: [ [Object] ]
  },

However, when I console req.file it showing "undefined"

new file-routers.js as below:

const express = require('express');
const multer = require('multer');
const router = express.Router();
const upload = multer({
  storage: multer.MemoryStorage,
}).fields([{name: "imagebackup"}, {name: "imagebanner"}]);


router.post('/file', (req, res)=>{
  
  upload(req, res, (err) => {
    console.log(req) // return Files [object null]
    console.log(req.file) // return "undefined" 

    if(err) throw err;
  })
    
});

**Weird thing is, by using upload.single(), everything works just fine. **

==

== ===== Here is the old code & can't solve it ===== It return an error

MulterError: Unexpected field
    at wrappedFileFilter (C:\Users\carchaw\Documents\pfx_template_generator_api\node_modules\multer\index.js:40:19)
    at Busboy.<anonymous> (C:\Users\carchaw\Documents\pfx_template_generator_api\node_modules\multer\lib\make-middleware.js:114:7)
    at Busboy.emit (node:events:379:20)

On the form submit, I need upload 2 images from different input field, create new prefix on GCS, and also store the image's name and other's details to be sent in request.body.

From the front-end part, I using Fetch as below:

const getFormContianer = document.getElementById("get_form")
 async function handleForm(e) {
    e.preventDefault();
    let dataForm = new FormData(e.target)
    
   await fetch(file_api, {
        method: 'POST',    
        body: dataForm
        
    }).then((res)=>{
        return res.json();
    }).then((data)=>{
        console.log('api err: '+data);
    }).catch((err) =>{
        console.log('api err: '+ err)
    })
    
}

getFormContianer.addEventListener('submit', handleForm)

index.html

<form id="get_form">
<label for="video_url">video_url</label>
<input name="video_url" type="text" id="video_url" value=""><br>
<label for="image_backup">image_backup</label>
<input name="image_backup" type="file" id="image_backup" value=""><br>
<label for="image_banner">image_banner</label>
<input name="image_banner" type="file" id="image_banner" value=""><br>
</form>
<input type="submit" id="handle_submit">

Nodejs

multer middleware

const util = require("util");
const multer = require("multer");

let processFile = multer({
  storage: multer.memoryStorage()
}).fields([{ name: "image_backup" }, { name: "image_banner" }])

let processFileMiddleware = util.promisify(processFile);
module.exports = processFileMiddleware;

handling Upload

const handleUploadImages = async (req, res) =>{
  try {
    await processFile(req, res);

    if (!req.file) {
      return res.status(400).send({ message: "Please upload a file!" });
    }

    // Create a new blob in the bucket and upload the file data.
    const blob = bucket.file(newFolderPath + req.file.originalname);
    const blobStream = blob.createWriteStream({
      resumable: false,
    });

    blobStream.on("error", (err) => {
      res.status(500).send({ message: err.message });
    });

    blobStream.on("finish", async (data) => {
      // Create URL for directly file access via HTTP.
      const publicUrl = format(
        `https://storage.googleapis.com/${bucket.name}/${newFolderPath}/${blob.name}`
      );

      try {
        // Make the file public
        await bucket.file(newFolderPath + req.file.originalname).makePublic();
      } catch {
        return res.status(500).send({
          message:
            `Uploaded the file successfully: ${newFolderPath + req.file.originalname}, but public access is denied!`,
          url: publicUrl,
        });
      }

      res.status(200).send({
        message: "Uploaded the file successfully: " + newFolderPath + req.file.originalname,
        url: publicUrl,
      });
    });

    blobStream.end(req.file.buffer);
  } catch (err) {
    res.status(500).send({
      message: `Could not upload the file: ${req.file.originalname}. ${err}`,
    });
  }
}

I did use express json and urlencoded on index.js

const express = require('express');
const cors = require('cors'); 
const config = require('./config')
const app = express()

const templates = require('./routes/templates-routes');
const files = require('./routes/files-routes');

// Middleware
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));  


app.use(express.static('public'))
app.use('/api', templates.routes);
app.use('/create', files.routes);


app.listen(config.port, () => {
    console.log(`Example app listening at http://localhost:${config.port}`)
  })

Hope that can get some suggestion on this, thank you!


Solution

  • Finally solved the issue after few days keep trying.

    for front-end POST rest API, passing files by appending file itself and the name.

     function handleForm(e) {
        e.preventDefault();
        let dataForm = new FormData(e.target)
       
        dataForm.append("image_backup", document.getElementById("image_backup").files[0]);    
        dataForm.append("image_banner", document.getElementById("image_banner").files[0]);
        dataForm.append("image_banner_name", document.getElementById("image_banner").value.replace(/^.*[\\\/]/, ''));
        dataForm.append("image_backup_name", document.getElementById("image_backup").value.replace(/^.*[\\\/]/, ''));
        
       await fetch(file_api, {
            method: 'POST',    
            body: dataForm
            
        }).then((res)=>{
            return res.json();
        }).then((data)=>{
            console.log('api data: '+ data);
        }).catch((err) =>{
            console.log('api err: '+ err)
        })
        
    }
    

    on Nodejs

    const multer = require('multer');
    const upload = multer({
      storage: multer.MemoryStorage,
    }).fields([{name: "image_backup"}, {name: "image_banner"}]);
    
    const startCreatefiles = async(req, res, next) =>{
     
      upload(req, res, (err) => {  
        
         console.log(req.body);
          console.log(req.files);
          
      })    
    }
    

    Then successfully get the text form data and file itself.