I have a project with a React front-end in a Docker container. From this UI, I am uploading pictures (jpg, png files) to a Node.js backend that lives in a separate Docker container. From there, the pictures are successfully saved to a shared volume, the 'public' folder of the React client. However, I'm unable to see the freshly-uploaded pictures in the UI (using a simple html tag: <img src="/racoon.jpg" />
).
The only way I can see the pictures from the UI is by rebuilding the entire project (docker compose build
). I think this is because Docker images are immutable. So once new files are added they're not immediately visible. But I need a solution that will host the pictures in real-time.
I'm not sure I would even need Docker for this. Maybe I could just start a Node server from the docker-compose somehow? I'm very new to Docker, any help?
My docker-compose.yml with a few things omitted for brevity:
x-services-volume: &services-volume
type: bind
source: ./client/public
target: /client/public
services:
server:
build: ./server
container_name: node_server_container
depends_on:
- mysql-db
- redis-cache
environment:
<<: *common-variables
MYSQL_HOST_IP: mysql-db
ports:
- 8000:8000
volumes:
- *services-volume
- ./server:/app
links:
- mysql-db
- redis-cache
command: npm start
client:
build: ./client
container_name: client_container
environment:
<<: *common-variables
NODE_PATH: src
ports:
- 3000:3000
volumes:
- *services-volume
- ./client/src:/app/src
links:
- server
command: npm start
Node.js/Express allows a public directory for serving static files (https://expressjs.com/en/starter/static-files.html). I found this works in a Docker container as well. I set up a folder called 'files' for this purpose.
In my index.js
app.use(express.static('files'));
I had to change my docker-compose.yml by no longer using a shared volume, but added the 'files' volume to my server container. Stuff omitted for brevity.
server:
build: ./server
container_name: node_server_container
depends_on:
- mysql-db
- redis-cache
environment:
<<: *common-variables
MYSQL_HOST_IP: mysql-db
ports:
- 8000:8000
volumes:
- ./server/files:/server/files/
- ./server:/app
links:
- mysql-db
- redis-cache
command: npm start
client:
build: ./client
container_name: client_container
environment:
<<: *common-variables
NODE_PATH: src
ports:
- 3000:3000
volumes:
- ./client/src:/app/src
links:
- server
command: npm start
Then I simply changed my server-side code to save the photos to the files directory:
this.fileRouter.post('/upload', this.authPolicy, (req, res) => {
if (!req.files || Object.keys(req.files.image).length === 0) {
return res.status(400).send('No files were uploaded.');
}
try {
let file = req.files.image;
let target_path = `/server/files/${file.name}`;
fs.writeFile(target_path, file.data, function (err) {
if (err) console.error(err.stack);
else {
console.log('File uploaded to ' + target_path);
}
});
res.send('File(s) uploaded!');
} catch (err) {
console.log(err);
return res.status(422).send('Cannot process the file');
}
});
Then I can see the photos like this: <img src='http://localhost:8000/llama.jpg' alt='image'/>