node.jsimageherokumulter

Uploading Images using Multer (works locally, but not when deployed on Heroku)


I'm a bit stuck with my blog app.

It works properly when I run it locally, but when it's deployed to Heroku it seems that the images that get uploaded via Multer aren't being uploaded into my images folder

When I create a post from the Heroku app, I can create the post - but the picture is missing

Below is my server code

const path = require("path");
const db = require('./config/connection');
const multer = require('multer')

const routes = require('./routes');

const app = express()
const PORT = process.env.PORT || 5000

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

app.use("/images", express.static(path.join(__dirname, "./images")));

const storage = multer.diskStorage({
    destination: (req, file, callback) => {
        callback(null, 'images')
    },
    filename: (req, file, callback) => {
        callback(null, req.body.name)
    }
})

const upload = multer({ storage: storage })
app.post('/upload', upload.single('file'), (req, res) => {
    res.status(200).json('File has been uploaded')
})

if (process.env.NODE_ENV === 'production') {
    app.use(express.static("client/build"));
}

app.use(routes)

db.once('open', () => {
    app.listen(PORT, () => {
        console.log(`API server running on port ${PORT}!`);
    });
});

Below here is my client code

import React, { useContext, useState } from 'react';
import './write.css';
import { Context } from '../../context/Context';

export default function Write() {
    const [title, setTitle] = useState('')
    const [description, setDescription] = useState('')
    const [file, setFile] = useState('')
    const { user } = useContext(Context)

    const handleSubmit = async (e) => {
        e.preventDefault()
        const newPost = {
            username: user.username,
            title, 
            description,
        }
        if(file){
            const data = new FormData()
            const filename = Date.now() + file.name
            data.append('name', filename)
            data.append('file', file)
            newPost.postPicture = filename
            try {
               await axios.post('/upload', data)
            } catch (error) {
                console.log(error)
            }
        }
        try {
            const response = await axios.post('/posts', newPost)
            window.location.replace('/posts/'+response.data._id)
        } catch (error) {
            console.log(error)
        }
    }
    return (
        <div className='write'>
            {file && 
            <img 
                className='write_image' 
                src={URL.createObjectURL(file)} 
                alt="" 
                />
            }
            <form className='write_form' onSubmit={handleSubmit}>
                <div className='write_form_group'>
                    <label htmlFor='file_input'>
                        <i className='write_icon far fa-plus-square'></i>
                    </label>
                    <input type='file' id='file_input' style={{display: 'none'}} onChange={e=>setFile(e.target.files[0])} />
                    <input type='text' placeholder='Title' className='write_input' onChange={e=>setTitle(e.target.value)}/>
                </div>
                <div className="write_form_group">
                    <textarea placeholder='Tell your story...' type='text' className='write_input write_text' onChange={e=>setDescription(e.target.value)}></textarea>
                </div>
                <button className='write_submit' type='submit'>Publish</button>
            </form>
        </div>
    )
}

I'm not sure what's going wrong, my brain is being pointed into the server side code where I use multer.diskStorage (maybe this doesn't work when the app is being hosted somewhere?)

Any advice would be amazing

Also here is the link to the whole repo on my github... because I feel like just looking at the code in these posts might be hard to follow

https://github.com/Wickette/wickettes_blog


Solution

  • Heroku doesn't let you do this. You need to create a bucket on Amazon's S3 service or another similar service to upload that.