admin管理员组

文章数量:1122846

I'm building a Next.js 14 app running in a Docker container where users can upload images. These images are stored outside the public folder in Server/uploads (shared via Samba on my home server), and I need them to be immediately available during runtime without having to rebuild the app. I'm using Apache as a reverse proxy to serve the images, but when trying to access them, I get a 404 error even though I see the image get successfully uploaded to the correct folder.

Image Upload Route:

// Set the base upload directory on the server-side
const baseUploadPath = path.join(__dirname, "../uploads");

if (!fs.existsSync(baseUploadPath)) {
  fs.mkdirSync(baseUploadPath, { recursive: true });
}

// Multer storage setup for image uploads
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    const userUploadPath = path.join(baseUploadPath, req.user.id.toString()); // Create user-specific folder
    if (!fs.existsSync(userUploadPath)) {
      fs.mkdirSync(userUploadPath, { recursive: true });
    }
    cb(null, userUploadPath); // Save to user-specific folder
  },
  filename: (req, file, cb) => {
    cb(null, Date.now() + "-" + file.originalname); // Unique filename
  },
});

const upload = multer({ storage });

// Create a new spot (Protected Route)
router.post("/", authMiddleware, upload.single("image"), async (req, res) => {
  try {
    const { spotName, description, location } = req.body;

    // Ensure location exists and split into latitude and longitude
    if (!location) {
      return res.status(400).json({
        message:
          "Location is required and should be in 'latitude, longitude' format.",
      });
    }

    const [latitude, longitude] = location
      .split(",")
      .map((coord) => parseFloat(coord.trim()));

    if (isNaN(latitude) || isNaN(longitude)) {
      return res.status(400).json({
        message: "Invalid coordinates format. Use 'latitude, longitude'.",
      });
    }

    // Generate the relative path for the image (e.g., /uploads/userId/image.jpg)
    const imagePath = req.file
      ? `/uploads/${req.user.id}/${req.file.filename}`
      : null;

    // Create the new spot
    const newSpot = await Spot.create({
      spotName,
      description,
      location,
      latitude,
      longitude,
      image: imagePath,
      userId: req.user.id, // Authenticated user ID
    });

    res.status(201).json(newSpot);
  } catch (error) {
    res.status(500).json({ message: "Failed to add the spot" });
  }
});

Here is my apache2 conf file:

<VirtualHost *:80>
    ServerName caligo.site
    ServerAlias www.caligo.site

    ProxyPreserveHost On

    # Update all backend routes to include /api prefix
    ProxyPass "/api/auth" "http://localhost:3001/api/auth"
    ProxyPassReverse "/api/auth" "http://localhost:3001/api/auth"

    ProxyPass "/api/comments" "http://localhost:3001/api/comments"
    ProxyPassReverse "/api/comments" "http://localhost:3001/api/comments"

    ProxyPass "/api/spots" "http://localhost:3001/api/spots"
    ProxyPassReverse "/api/spots" "http://localhost:3001/api/spots"

    ProxyPass "/api/users" "http://localhost:3001/api/users"
    ProxyPassReverse "/api/users" "http://localhost:3001/api/users"

    # Serve the uploads directory directly
    Alias "/uploads" "/home/sambauser/shared_folder/web servers/CaliGo/Server/uploads"
    <Directory "/home/sambauser/shared_folder/web servers/CaliGo/Server/uploads">
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted
    </Directory>

    EnableMMAP off

    # Route all other paths to frontend on port 3000
    ProxyPass "/" "http://localhost:3000/"
    ProxyPassReverse "/" "http://localhost:3000/"

</VirtualHost>

Issues:

  • The uploaded images are not accessible via the /uploads route and result in a 404 error.
  • The files are stored on my home server using Samba for file sharing, and the Apache server is configured to serve them.

Here is my Dockerfile in my client folder:

# Dockerfile for Next.js frontend
# Use the official Node.js image
FROM node:18-alpine

# Set the working directory
WORKDIR /app

# Set environment variable for production
ENV NODE_ENV=production

# Copy package.json and package-lock.json
COPY package*.json ./

# Install dependencies
RUN npm install 

# Copy the rest of the application code
COPY . .

# Build the Next.js app
RUN npm run build

# Expose the port Next.js runs on
EXPOSE 3000

# Start the Next.js app
CMD ["npm", "start"]

And here is my Server Dockerfile:

# Dockerfile for Node.js backend
FROM node:18-alpine

# Install dependencies for bcrypt on Alpine
RUN apk add --no-cache python3 make g++

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .

# Expose the backend port
EXPOSE 3001

# Wait for MySQL to be available, then start the server
CMD /bin/sh -c "while ! nc -z mysql 3306; do echo 'Waiting for MySQL...'; sleep 3; done && node server.js"

Finally here's my docker-compose file:

version: "3.8"

services:
  frontend:
    build:
      context: ./client
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    env_file:
      - .env  # Use .env for environment variables
    depends_on:
      - backend
    volumes:
      - ./client/public:/app/public  # Map the frontend public folder for static assets

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: db
      MYSQL_ROOT_PASSWORD: password
    volumes:
      - mysql_data:/var/lib/mysql  # Persistent storage for MySQL database
    ports:
      - "3307:3306"

  backend:
    build:
      context: ./Server
      dockerfile: Dockerfile
    ports:
      - "3001:3001"
    env_file:
      - .env  # Use .env for environment variables
    volumes:
      - ./Server/uploads:/app/uploads  # Persistent storage for uploaded images in backend
    depends_on:
      - mysql

volumes:
  mysql_data:  # Persistent storage for MySQL data

What I've tried:

  • Ensuring that the file permissions are correct on the uploads directory.
  • Testing the route with direct access to the images outside the container, which also results in a 404 error.

Additional Info:

  • The app is running on a home server using Samba for file sharing.
  • Apache is configured as a reverse proxy to serve the frontend and backend.
  • The images are stored outside the Next.js public folder but are being uploaded to the server's uploads directory, which is shared via Samba.

本文标签: docker404 Error when Serving Files Outside of public Directory in Nextjs 14Stack Overflow