-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5f6430c
commit b743cfe
Showing
2 changed files
with
151 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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("服务已启动")); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); | ||
} |