Skip to content

Commit

Permalink
feat(hw/interview): 大文件上传
Browse files Browse the repository at this point in the history
  • Loading branch information
changshou83 committed May 21, 2023
1 parent 5f6430c commit b743cfe
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 0 deletions.
47 changes: 47 additions & 0 deletions HandWriting/interview/bigFileUpload/backend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import http from "http";
import multiparty from "multiparty";
import path from "path";
import fs from "fs-extra";
import { fileURLToPath } from "url";

// ESM下模拟 __dirname
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const server = http.createServer();
const UPLOAD_DIR = path.resolve(__dirname, ".", "files");

server.on("request", async (req, res) => {
// 跨域
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Headers", "*");

if (req.url === "/upload") {
const multipart = new multiparty.Form();
// 解析 FormData
multipart.parse(req, async (err, fields, files) => {
if (err) return;
console.log("fields=", fields);
console.log("files=", files);

const file = files.file;
const fileName = fields.fileName;
const chunkName = fields.chunkName;

const chunkDir = path.resolve(UPLOAD_DIR, `${fileName}-chunks`);
if (!fs.existSync(chunkDir)) {
await fs.mkdir(chunkDir);
}

await fs.move(file.path, `${chunkDir}/${chunkName}`);
res.end(
JSON.stringify({
code: 0,
message: "切片上传成功",
})
);
});
}
});

server.listen(3000, () => console.log("服务已启动"));
104 changes: 104 additions & 0 deletions HandWriting/interview/bigFileUpload/frontend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
/* <input type="file" id="input"></input>
<button id="upload">上传</buttonsh>
<div id="progress"></div> */
}
let chunkList: Array<{ file: File }> = [];
interface HTMLInputEvent extends Event {
target: HTMLInputElement & EventTarget;
}
document.querySelector("#input")?.addEventListener("change", (evt) => {
const target = evt.target as HTMLInputElement;
if (target.files) {
chunkList = createChunk(target.files[0]);
}
});

class Chunk {
file: File;
size: number;
percent: number;
fileName: string;
chunkName: string;
index: number;
constructor(file: File, index: number) {
this.file = file;
this.size = file.size;
this.percent = 0;
this.fileName = file.name;
this.chunkName = `${file.name}-${index}`;
this.index = index;
}
}
document.querySelector("#upload")?.addEventListener("click", (evt) => {
uploadFile(chunkList.map(({ file }, index) => new Chunk(file, index)));
});

/**
* 创建文件切片
* @param {File} file
* @param {number} size
* @returns
*/
function createChunk(file: File, size: number = 2 * 1024 * 1024) {
const chunks: Array<{ file: File }> = [];
let cur = 0;
while (cur < file.size) {
chunks.push({
file: file.slice(cur, cur + size) as File,
});
cur += size;
}
return chunks;
}

/**
* 上传文件分片
* @param {Chunk[]} fileList
*/
async function uploadFile(fileList: Chunk[]) {
const list = fileList
.map(({ file, fileName, index, chunkName }) => {
const formData = new FormData();
formData.append("file", file);
formData.append("fileName", fileName);
formData.append("chunkName", chunkName);
return {
formData,
index,
};
})
.map(({ formData, index }) =>
request({
method: "POST",
url: "",
data: formData,
}).then((res) => {
let p = document.createElement("p");
p.innerHTML = `${fileList[index].chunkName}--${res.data.message}`;
document.querySelector("#progress")?.appendChild(p);
})
);
await Promise.all(list);
}

type RequestConfig = { method: "POST" | "GET"; url: string; data: any };
type Res = {
data: {
message: string;
};
};
/**
* fetch封装
* @param {RequestConfig} param0
* @returns
*/
async function request({ method, url, data }: RequestConfig): Promise<Res> {
return fetch(url, {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: data,
method,
}).then((res) => res.json());
}

0 comments on commit b743cfe

Please sign in to comment.