返回

在 Node 中的无畏探险:全面掌握文件上传与下载

前端

一、Node的世界:初探文件上传

在Node的世界里,我们可以通过多种方式实现文件上传。其中一种便是Express框架与Multer的强强联手。Express提供轻量且灵活的框架,而Multer则专门负责文件上传,且它支持文件存储、类型检查等多种功能。

1. 拥抱Express:搭建坚实基石

Express是一个轻量级框架,可以轻松创建一个Node服务器。它提供灵活的路由系统,支持HTTP请求处理,帮助我们构建RESTful API。

// 引入Express
const express = require('express');

// 创建Express实例
const app = express();

// 创建路由
app.post('/upload', upload.single('file'), (req, res) => {
  // 文件上传成功
  res.json({ success: true, file: req.file });
});

2. 携手Multer:文件上传的得力助手

Multer是一个用于Node的中间件,专门处理文件上传。它提供各种配置选项,使我们能够轻松处理文件上传请求,以及对上传文件进行存储和解析。

// 引入Multer
const multer = require('multer');

// 配置Multer选项
const storage = multer.diskStorage({
  destination: 'uploads/',
  filename: (req, file, cb) => {
    cb(null, Date.now() + '-' + file.originalname);
  },
});

// 创建Multer实例
const upload = multer({ storage });

二、深入解析:Node文件下载之道

如同文件上传一样,Node也为文件下载提供了多种途径。我们依旧可以选择Express框架,结合Stream或Buffer的方式,轻松实现文件下载。

1. Express再出马:构建下载通道

依然是熟悉的Express框架,我们可以使用它来创建文件下载路由,并在路由处理函数中,通过流式传输或缓冲区的方式,将文件数据发送给客户端。

// 引入Express
const express = require('express');

// 创建Express实例
const app = express();

// 创建下载路由
app.get('/download/:file', (req, res) => {
  const file = path.join(__dirname, 'uploads', req.params.file);
  res.sendFile(file);
});

2. Stream:源源不断的传输之道

Stream流式传输是一种高效且实用的数据传输方式,它可以一边读取文件一边将其发送给客户端,无需将整个文件加载到内存中。

// 引入文件系统模块
const fs = require('fs');

// 创建文件流
const fileStream = fs.createReadStream(file);

// 将文件流发送给客户端
res.writeHead(200, { 'Content-Type': 'application/octet-stream' });
fileStream.pipe(res);

3. Buffer:内存中的文件持有者

Buffer是一种缓冲区,它可以在内存中存储二进制数据。我们可以先将文件读入缓冲区,然后再将缓冲区发送给客户端。

// 引入文件系统模块
const fs = require('fs');

// 读取文件到缓冲区
const fileBuffer = fs.readFileSync(file);

// 将缓冲区发送给客户端
res.writeHead(200, { 'Content-Type': 'application/octet-stream' });
res.end(fileBuffer);

三、扬帆远航:Node文件传输的无限可能

Node在文件上传和下载领域大放异彩,我们还可以根据实际需求,进一步拓展应用场景。

1. 多文件上传与下载:一次处理,轻松自如

我们可以利用Express和Multer的组合,实现多文件上传的功能。在客户端使用HTML5的<input type="file" multiple>属性,就可以轻松选择多个文件进行上传。

对于文件下载,我们可以使用流式传输或缓冲区的方式,同时处理多个文件。

// 处理多文件上传
app.post('/upload-multiple', upload.array('files'), (req, res) => {
  // 多个文件上传成功
  res.json({ success: true, files: req.files });
});

// 处理多文件下载
app.get('/download-multiple', (req, res) => {
  const files = req.query.files.split(',');
  res.writeHead(200, { 'Content-Type': 'application/zip' });
  const zip = archiver('zip');
  zip.pipe(res);
  files.forEach((file) => {
    zip.append(fs.createReadStream(file), { name: file });
  });
  zip.finalize();
});

2. 断点续传:无惧网络波动

当网络连接不稳定时,断点续传可以确保文件传输不会中断。我们可以使用HTTP的Range头字段来实现断点续传。

// 断点续传
app.get('/download-resume', (req, res) => {
  const file = path.join(__dirname, 'uploads', req.params.file);
  const start = parseInt(req.headers['range'].split('=')[1].split('-')[0]);
  const end = parseInt(req.headers['range'].split('=')[1].split('-')[1]);
  const fileLength = fs.statSync(file).size;

  if (start >= fileLength) {
    res.status(416).end();
    return;
  }

  if (end >= fileLength) {
    end = fileLength - 1;
  }

  res.writeHead(206, {
    'Content-Type': 'application/octet-stream',
    'Content-Length': end - start + 1,
    'Content-Range': `bytes ${start}-${end}/${fileLength}`,
  });

  const fileStream = fs.createReadStream(file, { start, end });
  fileStream.pipe(res);
});

3. 文件预览:一睹为快

在某些场景下,我们可能需要在下载文件之前预览文件内容。我们可以利用Express和各种文件预览库,轻松实现文件预览功能。

// 文件预览
app.get('/preview/:file', (req, res) => {
  const file = path.join(__dirname, 'uploads', req.params.file);
  const mimeType = mime.getType(file);

  if (mimeType.startsWith('image/')) {
    res.writeHead(200, { 'Content-Type': mimeType });
    fs.createReadStream(file).pipe(res);
  } else if (mimeType.startsWith('video/')) {
    res.writeHead(200, { 'Content-Type': mimeType });
    const ffmpeg = require('fluent-ffmpeg');
    ffmpeg(file)
      .outputFormat('mp4')
      .videoCodec('libx264')
      .audioCodec('aac')
      .outputOptions('-movflags faststart')
      .size('320x240')
      .on('error', (err) => {
        console.error(err);
        res.status(500).end();
      })
      .on('progress', (progress) => {
        console.log(`Preview progress: ${progress.percent}%`);
      })
      .on('end', () => {
        console.log('Preview generated');
        res.end();
      })
      .pipe(res, { end: true });
  } else {
    res.status(400).end();
  }
});

结语

Node凭借其强大且灵活的特性,在文件上传和下载领域,为我们提供了丰富的选择和强大的功能。无论是简单的文件传输,还是复杂的多文件处理和文件预览,Node都能轻松应对。让我们一起探索Node的奥秘,在文件管理的广阔天地中乘风破浪。