Search code examples
node.jsfile-uploadserver

Node js: Type error when trying to upload files to a web server


I am new to node js and am trying to make a function that monitors a target folder and uploads newly saved files to a server. This breaks in the uploadFile function. I am unsure what I need to have in app.post as well. Thank you for the help!

Error uploading file: TypeError [ERR_INVALID_ARG_TYPE]: The "cb" argument must be of type function. Received undefined at maybeCallback (node:fs:180:3) at Object.readFile (node:fs:371:14) at uploadFile (/Users/.../server.js:99:35) at Timeout.uploadFromTarget [as _onTimeout] (/Users/.../server.js:86:5) at listOnTimeout (node:internal/timers:573:17) at process.processTimers (node:internal/timers:514:7) { code: 'ERR_INVALID_ARG_TYPE' }

const http = require('http');
const fs = require('fs');
const fetch = import('node-fetch');
const FormData = require('form-data');
const path = require('path');
const express = require('express');
const formidable = require('formidable');
const port = 80;

const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.static('/upload')); // Serve files from the upload directory

// Directories
const uploadDirectory = './upload/'; // Server upload directory
const targetDirectory = '/Users/.../test_tool_logs'; // Simulated target directory

// User Options
const checkInterval = 2 * 1000; // Check every 2 seconds
const uploadInterval = 5 * 1000; // Upload every 5 seconds
const rename_with_date = false; // Add datetime to file name in uploads folder

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

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

let previousFiles = [];
let changedFiles = [];

// Check for changes in the target directory
function checkForChanges() {
    fs.readdir(targetDirectory, (err, files) => {
        if (err) {
            console.error('Failed to read target directory:', err);
            return;
        }

        let currentFiles = files.map(file => {
            let filePath = path.join(targetDirectory, file);
            let stats = fs.statSync(filePath);
            return { name: file, mtime: stats.mtimeMs };
        });

        // Determine new or updated files
        let updates = currentFiles.filter(file => {
            let prev = previousFiles.find(f => f.name === file.name);
            return !prev || file.mtime > prev.mtime;
        });

        if (updates.length > 0) {
            updates.forEach(file => {
                if (!changedFiles.includes(file.name)) {
                    changedFiles.push(file.name);
                }
            });
            console.log('Detected new or updated files:', updates.map(f => f.name));
        }

        previousFiles = [...currentFiles];
    });
}

// Upload a file from changedFiles
function uploadFromTarget() {
    if (changedFiles.length === 0) {
        console.log('No files to upload.');
        return;
    }

    let fileName = changedFiles.shift();
    let sourcePath = path.join(targetDirectory, fileName);

    if (rename_with_date) {
        // Creates a date string in a "YYYY-MM-DD_HH-MM-SS" format
        const date = new Date();
        const dateString = date.toISOString().replace(/:/g, '-').replace('T', '_').substring(0, 19);
        
        const fileExtension = path.extname(fileName);
        fileName = `${path.basename(fileName, fileExtension)}_${dateString}${fileExtension}`;
    }

    uploadFile(sourcePath, uploadDirectory, fileName)
    
    console.log(`Files waiting to upload: ${changedFiles}`);
}

// Set intervals for checking changes and uploading files
setInterval(checkForChanges, checkInterval);
setInterval(uploadFromTarget, uploadInterval);


async function uploadFile(filePath, uploadUrl, fileName) {
    try {
      // Read file content asynchronously
      const fileBuffer = await fs.readFile(filePath);
      
      // Create a new FormData instance
      const formData = new FormData();
  
      // Append the file to the FormData instance
      // 'file' is the field name that the server expects for the uploaded file
      formData.append('file', fileBuffer, fileName);
  
      // Perform the fetch request to upload the file
      const response = await fetch(uploadUrl, {
        method: 'POST',
        body: formData, // Automatically sets 'Content-Type': 'multipart/form-data'
      });
  
      // Check the response
      if (!response.ok) {
        throw new Error(`Server responded with ${response.status}: ${response.statusText}`);
      }
  
      console.log(`Successfully uploaded ${fileName}`);
    } catch (error) {
      console.error('Error uploading file:', error);
    }
}

// POST route for handling file uploads
app.post('/upload', (req, res) => {
    const form = new formidable.IncomingForm();
  
    // File upload directory
    form.uploadDir = uploadDirectory;
  
    form.parse(req, (err, fields, files) => {
      if (err) {
        console.error('Error processing upload:', err);
        return res.status(500).send('An error occurred during the upload.');
      }
      // Log uploaded file info
      console.log('Uploaded file:', files.file);
      res.status(200).send('File uploaded successfully.');
    });
  });

// GET route for displaying uploaded files
app.get('/', (req, res) => {
    fs.readdir(uploadDirectory, (err, files) => {
        if (err) {
            console.error('Failed to list upload directory:', err);
            return res.sendStatus(500);
        }

        let fileLinks = files.map(file => `<li>${file}</li>`).join('');
        res.send(`
            <h2>Uploaded Files</h2>
            <ul>${fileLinks}</ul>
        `);
    });
});

// Start the server
app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}`);
    console.log('Monitoring files saved to ' + targetDirectory + '\n');
});

I started with code that was able to move files from one directory to another, but wanted to be able to do this to a server in the future. I have not been able to get this to work.


Solution

  • fs.readFile excepts a callback function which is not defined in your code thus you're getting undefined cb error

    const fileBuffer = await fs.readFile(filePath);
    

    You can either switch to use readFileSync as below

    const fileBuffer = fs.readFileSync(filePath);
    

    Follow this Link to read more about readFileSync