前端切片文件, 后端组合成完整文件
创建 client 和 server 目录
"scripts": {
"dev": "vite"
},
<script src="./src/app.js" type="module"></script>
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div>
<p>
<progress id="uploadProgess" value="0"></progress>
</p>
<p>
<input type="file" id="videoUploader" value="选择视频" />
</p>
<p>
<span id="uploadInfo"></span>
</p>
<p>
<button id="uploadBtn">上传视频</button>
</p>
</div>
<video
src=""
id="video-play"
autoplay
controls
style="display: none"
></video>
<script src="./src/app.js" type="module"></script>
</body>
</html>
// app.js
import { UPLOAD_INFO, ALLOWED_TYPE, CHUNK_SIZE, API } from "./config.js";
((doc) => {
const doms = {
uploadProgess: doc.getElementById("uploadProgess"),
videoUploader: doc.getElementById("videoUploader"),
uploadInfo: doc.getElementById("uploadInfo"),
uploadBtn: doc.getElementById("uploadBtn"),
videoPlay: doc.getElementById("video-play"),
};
let uploadSize = 0;
let videoUrlPlay = "";
const init = () => {
bindEvent();
};
function bindEvent() {
doms.uploadBtn.addEventListener("click", uploadVideo, false);
}
async function uploadVideo() {
// const file = doms.videoUploader.files[0];
const {
files: [file],
} = doms.videoUploader;
// 检测是否选择文件
if (!file) {
doms.uploadInfo.innerText = UPLOAD_INFO["NO_FILE"];
return;
}
// 检测文件类型
if (!ALLOWED_TYPE[file.type]) {
doms.uploadInfo.innerHTML = UPLOAD_INFO["FILE_TYPE_ERROR"];
return;
}
const { name, type, size } = file;
const fileName = new Date().getTime() + "_" + name;
doms.uploadProgess.max = size;
doms.uploadInfo.innerHTML = "";
while (uploadSize < size) {
// console.log(uploadSize);
// 文件切分
const fileChunk = file.slice(uploadSize, uploadSize + CHUNK_SIZE);
// 创建 FormData
const formData = createFormData({
name,
type,
size,
fileName,
uploadSize,
file: fileChunk,
});
try {
let res = await fetch(API.UPLOAD_VIDEO, {
method: "POST",
body: formData,
});
const { videoUrl } = await res.json();
videoUrlPlay = videoUrl;
} catch (e) {
doms.uploadInfo.innerText = `${UPLOAD_INFO["UPLOAD_FAILED"]}: ${e.message}`;
return;
}
uploadSize += fileChunk.size;
doms.uploadProgess.value = uploadSize;
doms.uploadInfo.innerText = UPLOAD_INFO["UPLOAD_SUCCESS"];
doms.videoUploader.value = null;
}
// console.log(videoUrlPlay);
doms.videoPlay.src = videoUrlPlay;
doms.videoPlay.style.display = "block";
}
function createFormData({ name, type, size, fileName, uploadSize, file }) {
const fd = new FormData();
fd.append("name", name);
fd.append("type", type);
fd.append("size", size);
fd.append("fileName", fileName);
fd.append("uploadSize", uploadSize);
fd.append("file", file);
return fd;
}
init();
})(document);
// config.js
export const UPLOAD_INFO = {
NO_FILE: "请先选择文件",
FILE_TYPE_ERROR: "不支持该类型文件",
UPLOAD_FAILED: "上传失败",
UPLOAD_SUCCESS: "上传成功",
};
export const ALLOWED_TYPE = {
"video/mp4": "mp4",
};
export const CHUNK_SIZE = 64 * 1024;
const BASE_URL = "http://localhost:8000/";
export const API = {
UPLOAD_VIDEO: BASE_URL + "upload_video",
};
"scripts": {
"dev": "nodemon ./app.js"
},
// app.js
const express = require("express");
const bodyParser = require("body-parser");
const uploader = require("express-fileupload");
const { extname, resolve } = require("path");
const { existsSync, appendFileSync, writeFileSync } = require("fs");
const app = express();
const PORT = 8000;
const ALLOWED_TYPE = {
"video/mp4": "mp4",
};
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(uploader());
app.use("/", express.static("upload_temp"));
// 跨域处理
app.all("*", (req, res, next) => {
res.header("Access-Control-Allow-origin", "*");
res.header("Access-Control-Allow-Methods", "POST,GET");
// next();
next();
});
app.post("/upload_video", (req, res) => {
const { name, type, size, fileName, uploadSize } = req.body;
const { file } = req.files;
// 检测是否发送文件
if (!file) {
res.send({
code: 1001,
msg: "no file uploaded",
});
return;
}
// 检测文件类型
if (!ALLOWED_TYPE[type]) {
res.send({
code: 1002,
msg: "file type error",
});
return;
}
const filename = fileName + extname(name);
const filePath = resolve(__dirname, "./upload_temp/", filename);
if (uploadSize !== "0") {
if (!existsSync(filePath)) {
res.send({
code: 1003,
msg: "no file exist",
});
return;
}
appendFileSync(filePath, file.data);
res.send({
code: 0,
msg: "append",
videoUrl: "http://localhost:8000/" + filename,
});
return;
}
writeFileSync(filePath, file.data);
res.send({
code: 0,
msg: "file is created",
});
});
// 启动服务器
app.listen(PORT, () => {
console.log("server is running on " + PORT);
});