0

EDIT

I removed app.use(fileUpload());. So it finally worked. But unfortunately in the folder images of the backend I only get these files c43jnfeh734hdfudf.

For this reason, nothing is displayed in the frontend.

const imagePath = req.file.path
const description = req.file.originalname

console.log(imagePath)
console.log(description)
images\c43jnfeh734hdfudf
empty

I have a problem. I would like to save images with a fixed URL on my server. I found the following code snippet, but unfortunately it doesn't work. I get the following error in the backend: 'TypeError: Cannot read property 'path' of undefined'.

The following values are 'undefined'. const imagePath = req.file.path const description = req.body.description How can I save an image as a URL on the server?

Here is the tutorial, where I found the code snippet https://github.com/meech-ward/sammeechward.com_mdx/blob/master/content/articles/uploading-images-express-and-react/index.mdx

React

import { useState } from 'react'
import axios from 'axios'

export default function App() {
  const [file, setFile] = useState()
  const [description, setDescription] = useState("")
  const [image, setImage] = useState()

  const submit = async event => {
    event.preventDefault()

    const formData = new FormData()
    formData.append("image", file)
    formData.append("description", description)

    const result = await axios.post('/api/images', formData, { headers: {'Content-Type': 'multipart/form-data'}})
    setImage(result.data.imagePath)
  }

  return (
    <div className="App">
      <form onSubmit={submit}>
        <input
          filename={file} 
          onChange={e => setFile(e.target.files[0])} 
          type="file" 
          accept="image/*"
        ></input>
        <input
          onChange={e => setDescription(e.target.value)} 
          type="text"
        ></input>
        <button type="submit">Submit</button>
      </form>
      { image && <img src={image}/>}
    </div>
  )
}

Backend

const express = require('express')
const fs = require('fs')
const multer = require('multer')

const upload = multer({ dest: 'images/' })

const app = express()

// app.use('/images', express.static('images'))
app.get('/images/:imageName', (req, res) => {
  // do a bunch of if statements to make sure the user is 
  // authorized to view this image, then

  const imageName = req.params.imageName
  const readStream = fs.createReadStream(`images/${imageName}`)
  readStream.pipe(res)
})

app.post('/api/images', upload.single('image'), (req, res) => {
  const imagePath = req.file.path
  const description = req.body.description

  // Save this data to a database probably

  console.log(description, imagePath)
  res.send({description, imagePath})
})

app.listen(8080, () => console.log("listening on port 8080"))

routes/Test.js

const express = require("express");
const router = express.Router();
module.exports = router;
const auth_util = require("../utilities/auth_util");
const pgclient = require("../app");
const multer = require('multer')
const upload = multer({ dest: 'images/' })

// app.use('/images', express.static('images'))
router.get('/images/:imageName', (req, res) => {
  // do a bunch of if statements to make sure the user is 
  // authorized to view this image, then

  const imageName = req.params.imageName
  const readStream = fs.createReadStream(`images/${imageName}`)
  readStream.pipe(res)
})

router.post('/api/images', upload.single('image'), (req, res) => {
  console.log(req.file)
  console.log(req.files)
  const imagePath = req.file.path
  const description = req.body.description

  // Save this data to a database probably

  console.log(description, imagePath)
  res.send({ description, imagePath })
})

// added the lines below
const path = require("path");

router.use(express.static(path.join(__dirname, 'build')));

router.get('/', function (req, res) {
  res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

app.js

const express = require("express");
const cors = require("cors");
//const fileUpload = require("express-fileupload");
const session = require("express-session");
const { Pool } = require("pg");



const app = express();

app.use(express.json());
//app.use(fileUpload());
//------------------------------CORS settings------------------------------
var whitelist = [
    "http://localhost:3000",
    "http://localhost:3001",
];
var corsOptions = {
    credentials: true,
    exposedHeaders: ["set-cookie"],
    origin: function (origin, callback) {
        if (whitelist.indexOf(origin) !== -1 || !origin) {
            callback(null, true);
        } else {
            // callback(null, true)
            callback(new Error("Not allowed by CORS!!"));
        }
    },
};
app.options("*", cors(corsOptions));

const pgclient = new Pool({
    user: process.env.DB_USER,
    host: process.env.DB_HOST,
    database: process.env.DB_DATABASE,
    password: process.env.DB_PASSWORD,
    port: process.env.DB_PORT,
});

module.exports = pgclient;


app.set("trust proxy", 1);


const testRoute = require("./routes/test");
app.use("/test", cors(corsOptions), testRoute);

app.get("/", cors(corsOptions), (req, res, next) => {
    res.send("Welcome");
});

module.exports = app;

Test
  • 571
  • 13
  • 32
  • 1
    If you look at the post request that you make, the form data has keys "image" and "description". It doesn't contain the key file. Try changing file.path to image and see if that works. – Henry Hunt Jan 09 '23 at 07:52
  • @DickChany thank you for your comment! So i change to `req.image` -> `undefinded` and `req.body` -> `[Object: null prototype] {}`. Or did I misunderstood you ? – Test Jan 09 '23 at 07:56
  • `req.file` -> `undefinded`. But `req.files` -> `{ image: { name: 'testImage.png', data: , size: 27375, encoding: '7bit', tempFilePath: '', truncated: false, mimetype: 'image/png', md5: '19ab7d8ec735', mv: [Function: mv] } }` – Test Jan 09 '23 at 08:08
  • just so I understand the imagePath you want to include in your response is this just the file name of the file they sent? Or do you want to send them the path to an image file on the server? – Henry Hunt Jan 09 '23 at 08:12
  • And here is this gem from the multer documentation: .single(fieldname) Accept a single file with the name fieldname. The single file will be stored in req.file. – Henry Hunt Jan 09 '23 at 08:14
  • So file is not getting stored in the req.file field because it's name is not "image", lol – Henry Hunt Jan 09 '23 at 08:15
  • I want to send the path to an image file on the server. I have now uploaded an image called `image.png`, but `req.file` is `undefinded`. – Test Jan 09 '23 at 08:19
  • sorry, I misread that, my bad. The fieldname has to be image not the filename. This is not the issue. – Henry Hunt Jan 09 '23 at 08:21
  • https://github.com/expressjs/multer/issues/1115 Based on this check that the folder name matches exactly. – Henry Hunt Jan 09 '23 at 08:29
  • And why are you using `express-fileupload` alongside `multer`? try to remove it and see what happens. – Mostafa Fakhraei Jan 11 '23 at 15:13
  • @MostafaFakhraei thanks for the hint. That worked. I can see now in the folder `images/c1f04acfcfdc3`. But unfortunately there is not the image set in the folder `images`. – Test Jan 12 '23 at 09:10

2 Answers2

1

According to this answer, multer uses a kind of cookie in its file uploads and out of date versions of the cookie cause the file upload to fail. Try clearing your browser's cookies.

multer - req.file always undefined

Edit: here is the script working on my end with some images: enter image description here

I did have to make one minor edit to get the example to work on chrome. To avoid the CORS policy, the front and back end must both be hosted at the same port. So, I added get route to statically serve the react page from the expressjs server:

const express = require('express')
const fs = require('fs')
const multer = require('multer')

const upload = multer({ dest: 'images/' })

const app = express()

// app.use('/images', express.static('images'))
app.get('/images/:imageName', (req, res) => {
    // do a bunch of if statements to make sure the user is 
    // authorized to view this image, then

    const imageName = req.params.imageName
    const readStream = fs.createReadStream(`images/${imageName}`)
    readStream.pipe(res)
})

app.post('/api/images', upload.single('image'), (req, res) => {
    console.log(req.file)
    console.log(req.files)
    const imagePath = req.file.path
    const description = req.body.description

    // Save this data to a database probably

    console.log(description, imagePath)
    res.send({ description, imagePath })
})

// added the lines below
const path = require("path");

app.use(express.static(path.join(__dirname, 'build')));

app.get('/', function (req, res) {
    res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

app.listen(8080, () => console.log("listening on port 8080"))
Henry Hunt
  • 143
  • 11
  • Thanks for your answer. I tried to clean my cookies. But agian. I did not work. `req.file.path` is agian `undefinded` – Test Jan 09 '23 at 08:40
  • Ok, I have just managed to reproduce the error. Updates soon. – Henry Hunt Jan 09 '23 at 09:19
  • Actually, I was only able to get undefined by leaving the file selection on no file chosen. – Henry Hunt Jan 09 '23 at 09:23
  • So did it work for you? You only selected one file and everything worked? – Test Jan 09 '23 at 09:32
  • yes, it immediately worked for me. – Henry Hunt Jan 09 '23 at 09:33
  • The file was uploaded to your server and you got the URL back? – Test Jan 09 '23 at 09:42
  • I mean I still get the CORS policy error from not hosting the react app and express app from the same port. But the server receives the image and req.file.path contains the image path. – Henry Hunt Jan 09 '23 at 09:53
  • Still the same error :/ `req.file` is `undefined` – Test Jan 09 '23 at 11:52
  • Is the problem, because I have this in the folder `routes/Test.js` ? – Test Jan 09 '23 at 11:55
  • Sorry, for the delayed response. Probably, not. As, I mentioned, I was never able to reproduce exactly your bug. I would check your console in the browser to see if the CORS policy is stopping axios from sending the image to the backend. – Henry Hunt Jan 11 '23 at 03:14
  • There is no `CORS` problem. All the other API calls are working. :/ – Test Jan 11 '23 at 11:07
1

First of all, you need to remove express-fileupload. There is no need to use it alongside multer.

To have the correct file with an extension in specified folder, you need to change this part of your code:

remove this line:

const upload = multer({ dest: 'images/' })

change it to:

// routes/Test.js

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'images')
  },
  filename: function (req, file, cb) {
    cb(null, file.originalname)
  }
})

const upload = multer({ storage: storage })

For conventional and standard way to prevent overwriting the same file names, you need to change filename to this:

filename: function (req, file, cb) {
    cb(null, `${Date.now()}-${file.originalname}`)
}
Mostafa Fakhraei
  • 3,409
  • 3
  • 11
  • 25
  • Thank you very much! The image is now displayed in the backend. Only in the frontend in the React part the image is not displayed yet. – Test Jan 12 '23 at 11:39
  • What I got back from the backend is `{description: 'test.png', imagePath: 'images\\test.png'}` – Test Jan 12 '23 at 11:41
  • The route for serving images on the server side starts with `test`, so you need to add this prefix to the `img` tag. something like this: `` or if the server is on another port, use like this: `/test/' + image}/>` – Mostafa Fakhraei Jan 12 '23 at 12:08
  • Thank you very much. My backend is running on port `4001`. I changed it to `{ image && }`. But I am still not able to see the image. Inside the frontend it looks like `{ image && }` – Test Jan 12 '23 at 12:27
  • inspect the `img` src, copy and paste the URL into another tab, and see if it works or not. – Mostafa Fakhraei Jan 12 '23 at 12:48