Search code examples
node.jsreactjsdockerdocker-composefile-upload

How to live-load photos from Docker containers?


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

Solution

  • 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.

    enter image description here

    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'/>