1. 前言
文件上传功能是 Web 应用必不可少的一部分。本文将介绍几种在 Web 开发中实现文件上传的方式,并给出相应的代码示例。
2. 简单的文件上传
文件上传最基本的实现方式就是通过 HTML form 提交表单,并指定 enctype 属性为 multipart/form-data。后端应用程序需要能够处理接收到的文件数据,通常都是通过读取 HTTP 请求的 body 来获取文件数据。
以下是 HTML 表单代码:
<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="Upload">
</form>
PHP 后端文件代码:
// 保存上传的文件
if ($_FILES['file']) {
$tempPath = $_FILES['file']['tmp_name'];
$destPath = 'uploads/' . $_FILES['file']['name'];
move_uploaded_file($tempPath, $destPath);
}
3. Dropzone.js 实现上传
3.1. Dropzone.js 简介
Dropzone.js 是一款基于 jQuery 的文件上传库,具有以下特点:
轻松实现拖拽文件上传
自动上传和手动上传两种方式
支持多文件上传
支持文件类型和大小限制
官方网站:https://www.dropzonejs.com/
3.2. 使用 Dropzone.js 实现上传
使用 Dropzone.js 实现文件上传需要导入 dropzone.css 和 dropzone.js 文件。
以下是 HTML 代码:
<form id="my-awesome-dropzone" class="dropzone" action="upload.php"></form>
JavaScript 代码:
// 初始化 Dropzone
Dropzone.options.myAwesomeDropzone = {
paramName: 'file', // 传递给后台的参数名
maxFilesize: 2, // 文件最大允许大小,单位为 MB
acceptedFiles: 'image/*', // 允许上传的文件类型
init: function() {
this.on('success', function(file, response) {
console.log('上传成功:' + response);
});
this.on('error', function(file, errorMessage) {
console.log('上传失败:' + errorMessage);
});
}
};
PHP 后端文件代码:
// 保存上传的文件
if ($_FILES['file']) {
$tempPath = $_FILES['file']['tmp_name'];
$destPath = 'uploads/' . $_FILES['file']['name'];
move_uploaded_file($tempPath, $destPath);
echo '文件上传成功!';
} else {
echo '文件上传失败!';
}
4. AJAX 实现文件上传
AJAX 是一种在不重新加载页面的情况下向服务器发送 HTTP 请求的技术。通过 AJAX 实现文件上传,可以在上传过程中显示上传进度,并且不需要刷新页面。
以下是 HTML 代码:
<form id="my-form">
<input type="file" id="file-input">
<button type="button" id="upload-btn">上传</button>
</form>
<progress id="progress-bar" max="100" value="0"></progress>
JavaScript 代码:
// 绑定上传按钮的点击事件
document.getElementById('upload-btn').addEventListener('click', function() {
var fileInput = document.getElementById('file-input');
var file = fileInput.files[0];
var formData = new FormData();
formData.append('file', file);
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', function(evt) {
var percent = evt.loaded / evt.total * 100;
document.getElementById('progress-bar').value = percent;
}, false);
xhr.addEventListener('load', function(evt) {
console.log('上传完成!');
}, false);
xhr.addEventListener('error', function(evt) {
console.log('上传失败!');
}, false);
xhr.open('POST', 'upload.php');
xhr.send(formData);
});
PHP 后端文件代码与第 2 种方法相同,此处不再赘述。
5. WebSocket 实现文件上传
WebSocket 是一种全双工通信协议,通过 WebSocket 实现文件上传可以实时显示上传进度,并且可以在上传过程中暂停、继续上传。
以下是 HTML 代码:
<form id="my-form">
<input type="file" id="file-input">
<button type="button" id="upload-btn">上传</button>
</form>
<progress id="progress-bar" max="100" value="0"></progress>
JavaScript 代码:
// 创建 WebSocket 连接
var socket = new WebSocket('ws://127.0.0.1:8080');
// 绑定上传按钮的点击事件
document.getElementById('upload-btn').addEventListener('click', function() {
var fileInput = document.getElementById('file-input');
var file = fileInput.files[0];
// 发送文件信息
socket.send(JSON.stringify({
filename: file.name,
filesize: file.size
}));
// 分段上传文件数据
var chunkSize = 16 * 1024;
var offset = 0;
var reader = new FileReader();
reader.onload = function(evt) {
if (evt.target.readyState === FileReader.DONE) {
socket.send(evt.target.result);
offset += chunkSize;
document.getElementById('progress-bar').value = offset / file.size * 100;
if (offset < file.size) {
readSlice(offset);
}
}
};
function readSlice(start) {
var slice = file.slice(start, start + chunkSize);
reader.readAsArrayBuffer(slice);
}
readSlice(0);
});
// 显示上传进度
socket.addEventListener('message', function(evt) {
var data = JSON.parse(evt.data);
if (data.type === 'progress') {
document.getElementById('progress-bar').value = data.percent;
}
});
// 断开 WebSocket 连接
window.addEventListener('beforeunload', function() {
socket.close();
});
Node.js 服务端代码:
const WebSocket = require('ws');
const fs = require('fs');
const wss = new WebSocket.Server({ port: 8080 });
let currentSocket;
let currentSize = 0;
wss.on('connection', function connection(ws) {
currentSocket = ws;
console.log('Client connected');
});
wss.on('close', function close() {
console.log('Client disconnected');
});
wss.on('error', function error(err) {
console.log(err.message);
});
wss.on('message', function incoming(message) {
if (currentSocket) {
// 接收文件信息
const data = JSON.parse(message);
console.log(`File name: ${data.filename}, File size: ${data.filesize}`);
currentSize = 0;
// 创建一个可写流写入文件
const dest = fs.createWriteStream(`uploads/${data.filename}`);
currentSocket.on('message', function incoming(chunk) {
// 接收分段上传的文件数据
dest.write(chunk);
currentSize += chunk.length
currentSocket.send(JSON.stringify({
type: 'progress',
percent: currentSize / data.filesize * 100
}));
if (currentSize === data.filesize) {
dest.end();
console.log('File upload finished');
currentSocket = null;
}
});
} else {
console.log('No client connected');
}
});
6. 总结
Web 开发中实现文件上传的方式有多种,每种方式都有自己的优缺点和适用场景。在选择实现方式时,需要根据具体需求进行选择。