阿里云主机折上折
  • 微信号
Current Site:Index > File upload and download processing

File upload and download processing

Author:Chuan Chen 阅读数:41245人阅读 分类: Node.js

File Upload Handling

File upload is a common functional requirement in web development. Koa2 simplifies the file upload process through its middleware mechanism. The koa-body middleware can be used to easily implement file upload functionality. First, install the dependency:

npm install koa-body

Basic configuration example:

const Koa = require('koa');
const koaBody = require('koa-body');
const app = new Koa();

app.use(koaBody({
  multipart: true,
  formidable: {
    maxFileSize: 200*1024*1024, // Set upload file size limit, default 2M
    keepExtensions: true // Preserve file extensions
  }
}));

app.use(async ctx => {
  if (ctx.method === 'POST' && ctx.url === '/upload') {
    const file = ctx.request.files.file; // Get the uploaded file
    // File processing logic...
    ctx.body = 'File uploaded successfully';
  }
});

app.listen(3000);

Frontend HTML form example:

<form action="/upload" method="post" enctype="multipart/form-data">
  <input type="file" name="file">
  <button type="submit">Upload</button>
</form>

For handling multiple file uploads, use the multiple attribute in the frontend:

<input type="file" name="files" multiple>

Backend processing code:

const files = ctx.request.files.files; // Get the array of files
files.forEach(file => {
  // Process each file
});

File Upload Security Considerations

File upload functionality requires special attention to security issues:

  1. File type validation:
const ALLOWED_TYPES = ['image/jpeg', 'image/png'];
if (!ALLOWED_TYPES.includes(file.type)) {
  ctx.throw(400, 'Unsupported file type');
}
  1. Rename files to prevent directory traversal attacks:
const path = require('path');
const fs = require('fs');

const ext = path.extname(file.name);
const newFilename = `${Date.now()}${ext}`;
const filePath = path.join(__dirname, 'uploads', newFilename);

fs.renameSync(file.path, filePath);
  1. File content inspection:
const fileBuffer = fs.readFileSync(file.path);
if (fileBuffer.toString('utf8', 0, 2) === '#!') {
  ctx.throw(400, 'Potential executable file');
}

File Download Handling

Koa2 provides multiple ways to handle file downloads. The simplest method is to set response headers directly:

app.use(async ctx => {
  if (ctx.path === '/download') {
    const filePath = path.join(__dirname, 'files', 'example.pdf');
    ctx.set('Content-Disposition', 'attachment; filename="example.pdf"');
    ctx.body = fs.createReadStream(filePath);
  }
});

For large file downloads, use streaming:

const fileStream = fs.createReadStream(filePath);
fileStream.on('error', err => {
  ctx.throw(404, 'File does not exist');
});
ctx.set('Content-Length', fs.statSync(filePath).size);
ctx.body = fileStream;

Resumable Download Implementation

For large file downloads, implementing resumable downloads can enhance user experience:

app.use(async ctx => {
  if (ctx.path === '/download') {
    const filePath = path.join(__dirname, 'files', 'large.zip');
    const stat = fs.statSync(filePath);
    const range = ctx.headers.range;
    
    if (range) {
      const parts = range.replace(/bytes=/, "").split("-");
      const start = parseInt(parts[0], 10);
      const end = parts[1] ? parseInt(parts[1], 10) : stat.size - 1;
      const chunksize = (end - start) + 1;
      
      ctx.set('Content-Range', `bytes ${start}-${end}/${stat.size}`);
      ctx.set('Accept-Ranges', 'bytes');
      ctx.set('Content-Length', chunksize);
      ctx.status = 206;
      
      const file = fs.createReadStream(filePath, {start, end});
      ctx.body = file;
    } else {
      ctx.set('Content-Length', stat.size);
      ctx.body = fs.createReadStream(filePath);
    }
  }
});

File Download Progress Display

The frontend can display download progress using XMLHttpRequest:

const xhr = new XMLHttpRequest();
xhr.open('GET', '/download/large.zip', true);
xhr.responseType = 'blob';

xhr.onprogress = function(e) {
  if (e.lengthComputable) {
    const percentComplete = (e.loaded / e.total) * 100;
    console.log(`${percentComplete}% downloaded`);
  }
};

xhr.onload = function() {
  if (this.status === 200) {
    const blob = this.response;
    const a = document.createElement('a');
    a.href = window.URL.createObjectURL(blob);
    a.download = 'large.zip';
    a.click();
  }
};

xhr.send();

Compressed File Download

Use the archiver library to download multiple files as a compressed archive:

const archiver = require('archiver');

app.use(async ctx => {
  if (ctx.path === '/download-zip') {
    const archive = archiver('zip', {
      zlib: { level: 9 } // Set compression level
    });
    
    ctx.set('Content-Type', 'application/zip');
    ctx.set('Content-Disposition', 'attachment; filename="files.zip"');
    
    archive.pipe(ctx.res);
    archive.directory('public/files/', false);
    archive.finalize();
  }
});

File Upload Progress Monitoring

The frontend can monitor upload progress using FormData and XMLHttpRequest:

const formData = new FormData();
formData.append('file', fileInput.files[0]);

const xhr = new XMLHttpRequest();
xhr.open('POST', '/upload', true);

xhr.upload.onprogress = function(e) {
  if (e.lengthComputable) {
    const percentComplete = (e.loaded / e.total) * 100;
    console.log(`${percentComplete}% uploaded`);
  }
};

xhr.onload = function() {
  if (xhr.status === 200) {
    console.log('Upload successful');
  }
};

xhr.send(formData);

Cloud Storage Integration

Example of integrating AWS S3 storage:

const AWS = require('aws-sdk');
const s3 = new AWS.S3({
  accessKeyId: 'YOUR_ACCESS_KEY',
  secretAccessKey: 'YOUR_SECRET_KEY'
});

app.use(async ctx => {
  if (ctx.method === 'POST' && ctx.url === '/upload-s3') {
    const file = ctx.request.files.file;
    const params = {
      Bucket: 'your-bucket-name',
      Key: `uploads/${Date.now()}_${file.name}`,
      Body: fs.createReadStream(file.path)
    };
    
    try {
      const data = await s3.upload(params).promise();
      ctx.body = { url: data.Location };
    } catch (err) {
      ctx.throw(500, 'Failed to upload to S3');
    }
  }
});

File Management API Design

A complete file management API example:

const router = require('koa-router')();
const File = require('../models/file'); // Assuming a File model exists

router.post('/files', async ctx => {
  const file = ctx.request.files.file;
  const newFile = await File.create({
    name: file.name,
    size: file.size,
    type: file.type,
    path: `/uploads/${file.name}`
  });
  ctx.body = newFile;
});

router.get('/files', async ctx => {
  const files = await File.find();
  ctx.body = files;
});

router.get('/files/:id', async ctx => {
  const file = await File.findById(ctx.params.id);
  if (!file) ctx.throw(404);
  
  ctx.set('Content-Disposition', `attachment; filename="${file.name}"`);
  ctx.body = fs.createReadStream(path.join(__dirname, file.path));
});

router.delete('/files/:id', async ctx => {
  const file = await File.findByIdAndRemove(ctx.params.id);
  if (!file) ctx.throw(404);
  
  fs.unlinkSync(path.join(__dirname, file.path));
  ctx.status = 204;
});

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.