原生nodejs上传文件 不使用框架

示例代码:

const http = require('http');
const fs = require('fs');
const path = require('path');
const url = require('url');


// 设置公开目录
const publicDir = path.join(__dirname, 'public');
const uploadDir = path.join(publicDir, 'uploads');

// 检查并创建 uploads 文件夹
if (!fs.existsSync(uploadDir)) {
    fs.mkdirSync(uploadDir, { recursive: true });
    console.log('Uploads directory created.');
}

const server = http.createServer((req, res) => {
    const parsedUrl = url.parse(req.url, true);

    // 处理文件上传
    if (req.method === 'POST' && parsedUrl.pathname === '/upload') {
        const chunks = [];
        let boundary;

        // 获取请求头中的 boundary
        const contentType = req.headers['content-type'];
        if (contentType && contentType.startsWith('multipart/form-data')) {
            boundary = contentType.split('boundary=')[1];
        } else {
            res.writeHead(400, { 'Content-Type': 'text/plain' });
            res.end('Invalid content type');
            return; // 提前退出
        }

        // 接收数据
        req.on('data', (chunk) => { chunks.push(chunk); });
        req.on('end', () => {
            const body = Buffer.concat(chunks); // 将数据块合并为完整的 Buffer
            const boundaryBuffer = Buffer.from(`--${boundary}`);

            // 分割请求体
            const parts = splitBuffer(body, boundaryBuffer);

            let fileSaved = false;

            parts.forEach((part) => {
                if (part.includes('filename=')) {
                    // 提取文件名
                    const header = part.toString().split('\r\n\r\n')[0];
                    const filenameMatch = header.match(/filename="(.+)"/);
                    if (!filenameMatch) {
                        if (!fileSaved) {
                            res.writeHead(400, { 'Content-Type': 'text/plain' });
                            res.end('Filename not found');
                        }
                        return; // 提前退出
                    }
                    const filename = filenameMatch[1];

                    // 提取文件数据
                    const fileData = part.slice(part.indexOf('\r\n\r\n') + 4, part.lastIndexOf('\r\n'));

                    // 保存文件
                    const filePath = path.join(uploadDir, filename);
                    fs.writeFileSync(filePath, fileData);

                    console.log(`File saved: ${filePath}`);
                    fileSaved = true;

                    // 返回图片的访问 URL
                    res.writeHead(200, { 'Content-Type': 'application/json' });
                    res.end(JSON.stringify({
                        message: 'File uploaded successfully',
                        url: `http://localhost:3000/uploads/${filename}`
                    }));
                }
            });

            if (!fileSaved) {
                res.writeHead(400, { 'Content-Type': 'text/plain' });
                res.end('No file uploaded');
            }
        });
    }else {
        res.writeHead(404, { 'Content-Type': 'text/plain' });
        res.end('Not Found');
    }
});

/**
 * 使用指定的分隔符分割 Buffer
 * @param {Buffer} buffer - 要分割的 Buffer
 * @param {Buffer} delimiter - 分隔符 Buffer
 * @returns {Buffer[]} - 分割后的 Buffer 数组
 */
function splitBuffer(buffer, delimiter) {
    const result = [];
    let start = 0;
    let index = 0;

    while ((index = buffer.indexOf(delimiter, start)) !== -1) {
        if (start !== index) {
            result.push(buffer.slice(start, index));
        }
        start = index + delimiter.length;
    }

    if (start < buffer.length) {
        result.push(buffer.slice(start));
    }

    return result;
}

server.listen(3000, () => {
    console.log('Server is running on http://localhost:3000');
});