You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何改进用于局域网文件共享的Node.js HTTP服务器?

Got it, let's break down each of your issues and fix them using just Node.js's built-in HTTP module (since you can't use Express):


1. How to stop the server after other devices finish downloading

You've got a few flexible options here depending on your needs:

  • Auto-stop when all downloads complete
    Track active connections and shut down once the last download finishes. This works great if you only need to serve the file once or to a small number of devices:
    let activeConnections = 0;
    const server = http.createServer((req, res) => {
      activeConnections++;
      
      // ... your file transfer logic ...
      
      res.on('finish', () => {
        activeConnections--;
        if (activeConnections === 0) {
          console.log('All downloads done—shutting down server');
          server.close(() => {
            console.log('Server stopped successfully');
            process.exit(0);
          });
        }
      });
    });
    
  • Manual shutdown endpoint
    Add a simple secret route you can hit from any device to trigger a shutdown (good for testing or when you want control):
    const server = http.createServer((req, res) => {
      if (req.url === '/shutdown') {
        res.writeHead(200, {'Content-Type': 'text/plain'});
        res.end('Server shutting down...');
        server.close(() => {
          console.log('Server stopped via shutdown endpoint');
          process.exit(0);
        });
        return;
      }
      
      // ... your file transfer logic ...
    });
    
  • Idle timeout auto-stop
    If no requests come in for a set period, shut down automatically (perfect for unattended use):
    let idleTimeout;
    const resetIdleTimer = () => {
      clearTimeout(idleTimeout);
      idleTimeout = setTimeout(() => {
        console.log('No activity—shutting down server');
        server.close(() => process.exit(0));
      }, 5 * 60 * 1000); // 5-minute timeout
    };
    
    const server = http.createServer((req, res) => {
      resetIdleTimer();
      // ... your file transfer logic ...
    });
    resetIdleTimer(); // Start the timer when server boots
    

2. Fixing "invalid characters" error for non-English filenames

HTTP headers don't support raw non-ASCII characters directly. You need to encode the filename following the RFC 5987 standard to avoid this error. Here's how to adjust your Content-Disposition header:

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

const filePath = './你好.pdf'; // Example non-English filename
const rawFileName = path.basename(filePath);
// Encode the filename for safe HTTP header transmission
const encodedFileName = encodeURIComponent(rawFileName);

const server = http.createServer((req, res) => {
  fs.stat(filePath, (err, stats) => {
    if (err) {
      res.writeHead(404);
      res.end('File not found');
      return;
    }

    res.writeHead(200, {
      'Content-Type': 'application/octet-stream',
      'Content-Length': stats.size,
      // Use the RFC 5987 format for non-ASCII filenames
      'Content-Disposition': `attachment; filename*=UTF-8''${encodedFileName}`
    });

    // Stream the file to the client
    fs.createReadStream(filePath).pipe(res);
  });
});

This filename*=UTF-8''${encodedFileName} format is the official way to send non-English filenames in HTTP headers, and it'll work across all modern browsers/devices.


3. Boosting download speed with the Node.js HTTP module

Slow speeds are almost always caused by not using streaming or missing key optimizations. Here's how to fix it:

  • Use streaming instead of loading the whole file into memory
    Avoid fs.readFile (which loads the entire file into RAM) and use fs.createReadStream instead. This sends the file in chunks, which is way more efficient for large files.
  • Set critical response headers
    Always include Content-Length so the client knows exactly how much data to expect—this helps with progress tracking and efficient transfer. Adding Accept-Ranges: bytes also enables resumable downloads, which is a big win for interrupted transfers.
  • Tweak stream chunk size
    The default highWaterMark (chunk size) is 64KB. Increasing it to 1MB can improve throughput for large files:
    fs.createReadStream(filePath, { highWaterMark: 1024 * 1024 }) // 1MB chunks
      .pipe(res);
    
  • Disable Nagle's algorithm
    Node.js enables this by default, which delays small packets to reduce overhead. Disabling it can lower latency for file transfers:
    server.on('connection', (socket) => {
      socket.setNoDelay(true); // Turn off Nagle's algorithm
    });
    

Optimized full example

Here's all the fixes combined into one working server:

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

const filePath = './large-video.mp4';
const rawFileName = path.basename(filePath);
const encodedFileName = encodeURIComponent(rawFileName);

let activeConnections = 0;
let idleTimeout;

const resetIdleTimer = () => {
  clearTimeout(idleTimeout);
  idleTimeout = setTimeout(() => {
    console.log('No activity—shutting down');
    server.close(() => process.exit(0));
  }, 5 * 60 * 1000);
};

const server = http.createServer((req, res) => {
  resetIdleTimer();
  activeConnections++;

  // Handle manual shutdown
  if (req.url === '/shutdown') {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Server shutting down...');
    activeConnections--;
    server.close(() => {
      console.log('Server stopped');
      process.exit(0);
    });
    return;
  }

  // Serve the file
  fs.stat(filePath, (err, stats) => {
    if (err) {
      res.writeHead(404);
      res.end('File not found');
      activeConnections--;
      return;
    }

    res.writeHead(200, {
      'Content-Type': 'application/octet-stream',
      'Content-Length': stats.size,
      'Content-Disposition': `attachment; filename*=UTF-8''${encodedFileName}`,
      'Accept-Ranges': 'bytes'
    });

    const stream = fs.createReadStream(filePath, { highWaterMark: 1024 * 1024 });
    stream.pipe(res);

    // Track stream completion
    stream.on('end', () => {
      activeConnections--;
      if (activeConnections === 0) {
        console.log('All downloads done—shutting down');
        server.close(() => process.exit(0));
      }
    });

    stream.on('error', (err) => {
      console.error('Stream error:', err);
      res.writeHead(500);
      res.end('Error reading file');
      activeConnections--;
    });
  });
});

// Disable Nagle's algorithm for faster transfers
server.on('connection', (socket) => {
  socket.setNoDelay(true);
});

server.listen(8080, '0.0.0.0', () => {
  console.log(`Server running at http://${server.address().address}:8080`);
  resetIdleTimer();
});

内容的提问来源于stack exchange,提问作者Un1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:07:03