Search code examples
node.jsbackendmulter

Images not showing up in folder using Multer and nodejs


I have a nodejs backend with Multer to upload images to a local folder named /uploads and after that, I store the image name in my MySQL database. When I run this on localhost the file gets uploaded to the folder and everything seems to work, but after I push the code to Heroku, the image doesn't get uploaded to the folder anymore. I can still access the file when I fetch the URL of where the file should be, but I can't see it. and when I push new code to Heroku then since the image is not in the folder. I'm guessing it gets overridden.

Anyone can explain to me why or how? thanks!

app.post('/api/register', upload.single('avatar'), function (req, res, next) {
    
    pool.getConnection(async (err, connection) => {
        if (err) throw err

        console.log(req.file);

        const username = req.body.username;
        var password = req.body.password;
        const email = req.body.email;
        const picture = req.file.filename;

        const salt = await bcrypt.genSalt();
        password = await bcrypt.hash(password, salt);

        connection.query(`INSERT INTO users (username, password, email, picture) VALUES (?,?,?,?)`,
            [username, password, email, picture], (err, rows) => {
                connection.release() // return the connection to pool
                if (!err) {
                    console.log("insert succes");
                    res.send(`user with the record ID  has been added.`)
                } else {
                    console.log(err)
                }
            })
    })
})
const storage = multer.diskStorage({
    destination(req, file, callback) {
        callback(null, './uploads');
    },
    filename(req, file, callback) {
        callback(null, `${file.fieldname}_${Date.now()}_${file.originalname}`);
    },
});

const upload = multer({ storage })
const app = express()


app.use('/uploads', express.static(process.cwd() + '/uploads'));
app.use(express.json());
app.use(cors());

Solution

  • As mentioned in the comment via the link, because Heroku is more of a 'container' all storage is ephemeral (meaning it on the running container and is not persistent) So each time a container spins up its just loading your base code into the running image nothing more nothing less. When the image restarts or rebuilds it's a fresh instance and any items added while it was running are basically lost..

    So, unless your application is 'stateless' then will need to seperate things like

    Sessions Persistent storage

    Fortunately, this is not too difficult to setup. For sessions you would want to use something like Redis to store session information and for images / assets you can use S3 or other object store. Multer has a good s3 connector ( works with any s3 compatible object store), then in your DB you just store the file name and to return it, use the s3 url ( media.example.com/images/[image]) in your application..

    Now, to your question.. You should still be able to upload files to the running container (unless permissions prevent you ) there are use-cases where it does make sense, for example its just a temp file that will be read right away in the same thread as the request etc.

    From your code it looks like you are trying to upload to the 'uploads' folder off the root context of your app, so I would first ensure you have an 'uploads' folder and it has permissions to read / write, also the correct owner as well. Node should give you an error message in the console if there is an error so you will need to SSH to the running heroku instance to see what is going on from that point.