Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions apps/upload-file-vue/src/pages/UploadFile.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<!-- 看完最大的感受是,一味追求实现高阶功能,反而把诸多基础能力都丢了 -->
<!-- 这个文件实在太乱了,建议重写 -->
<template>
<div class="upload-container">
<div class="wrap">
Expand Down Expand Up @@ -55,6 +57,7 @@ import {
FIND_FILE,
CHUNK_INDEX,
MERGE_FILE
// 千万不要直接导入另一个包的 src,这是一个非常不好的实践
} from "@project/upload-file/src/setting";
const fileArray = ref<Array<File> | null>(null); // 文件
const fileChunksArray = ref<FilePieceArray[]>([]); // 文件切片
Expand All @@ -65,6 +68,8 @@ const loadData = async () => {
const data: any = await indexDB.get('fileChunksArray');
if (data) {
fileChunksArray.value = data.content.map((item: any) => {
// 从调用规则看,这里会创建很多个 ws 连接实例,这反而会有严重性能问题吧?网络连接本身就是比较耗时的操作啊
// 理论上应该建立一个,之后无限复用即可
connectWebSocket({
hash: item.hash,
name: item.fileName,
Expand All @@ -87,6 +92,7 @@ function handleFileChange(e: any) {
}

// 预处理文件
// 这些跟组件不强耦合的逻辑都应该拆出去
async function pretreatmentFile() {
// 文件为空直接弹出
if (!fileArray.value) return;
Expand Down Expand Up @@ -125,13 +131,16 @@ async function pretreatmentFile() {
}

// 文件上传
// 参数名 row 是什么意思?跟文件有什么关系?
async function uploadFile(row: FilePieceArray) {
const piecesLength = row.pieces.length;
if (piecesLength == row.totalIndex) {
// 更新视图
row.status = 'success';
row.percentage = 100;
// 通知服务端合并文件
// 看的好迷糊啊,ws 是个全局对象吗?上面的 connectWebSocket 的作用是啥?
// 真的有必要为每一个分片都创建 ws 对象吗?
ws.value[row.index].send(
JSON.stringify({
type: MERGE_FILE,
Expand Down Expand Up @@ -214,12 +223,16 @@ const connectWebSocket = async ({
index
}, isAgain = false) => {
await new Promise<void>((resolve, reject) => {
// 这个 host name 是认真的吗
const wsApi = new WebSocket(`ws://localhost:3000/websocket/${hash}_${name}`)
// 如果要用 ws,不要耦合到组件里,抽出去作为独立utils,独立文件管理
wsApi.onmessage = (e) => {
const data = JSON.parse(e.data).data;
if (data.type === FIND_FILE) {
// 查找文件
if (data.exists) {
// 这又是一个特别特别不好的实践,在函数里面更改了外部对象(fileChunksArray)的状态,这会导致内外耦合度过高
// 要合理使用闭包!
fileChunksArray.value[index].status = 'success';
fileChunksArray.value[index].percentage = 100;
} else {
Expand Down
1 change: 1 addition & 0 deletions apps/upload-file/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import KoaWebsocket from "koa-websocket";

// middlewares为中间件文件夹
import { errorCatch } from "./middlewares/error-catch"; // 捕获报错
// configProvider 没有用到
import { configProvider } from "./middlewares/config"; // 提供文件存储位置
import { defineWebSocketRoutes } from "./controllers"; // 定义路由

Expand Down
12 changes: 12 additions & 0 deletions apps/upload-file/src/controllers/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// 通篇看下来,感觉就是杂乱,各种逻辑放在一起
// 特别是解析参数那里,让人看的很迷糊
// 可读性差,建议重构
import { koaBody } from "koa-body"; // 解析提交中间件
import Koa from "koa";
import Router from "@koa/router";
Expand Down Expand Up @@ -25,20 +28,23 @@ export const defineWebSocketRoutes = (
// 缓存文件切片信息
let cache: any = {};
const router = new Router() as any;
// 这段代码耦合度过高,啥都写在这了,建议拆解优化
router.all("/websocket/:id", async (ctx) => {
// 通过ctx.params.id获取到前端传过来的id
const hashORname = ctx.params.id;
const HASH = hashORname.split("_")[0];
const NAME = hashORname.split("_")[1];
console.log(`文件名:${NAME},HASH:${HASH},建立了链接`);
// 立刻查询文件是否已上传
// ?为啥要立即查询?
findFileController(
{
hash: HASH,
name: NAME,
index: undefined,
},
fileStorageRoot
// 不要用 then
).then((res) => {
ctx.websocket.send(JSON.stringify(res));
});
Expand All @@ -48,6 +54,7 @@ export const defineWebSocketRoutes = (
let flg = "";
let sendData: any = null;

// 这种逻辑,应该拆出去
const determine = (val: string | Blob): string =>
Object.prototype.toString.call(val).slice(8, -1);

Expand All @@ -65,27 +72,32 @@ export const defineWebSocketRoutes = (
// 字符串信息
if (data.type === FIND_FILE) {
console.log("查找文件");
// 这里又 find 了一次?跟上面不是冲突了吗
findFileController(sendData, fileStorageRoot).then((res) => {
ctx.websocket.send(JSON.stringify(res));
});
} else if (data.type === CHUNK_INDEX) {
console.log(`记录分片index-${sendData.ind}`);
// 这个 cache 的作用是啥?
cache = sendData;
} else if (data.type === MERGE_FILE) {
console.log("合并文件");
mergeChunksController(sendData, fileStorageRoot);
} else {
console.error("未知的消息类型");
}
// 好乱啊。。。为啥是根据消息数据“类型”来判断的?为啥不用一些字段来标识呢
} else if (flg === "blob") {
console.log("分片上传");
saveChunkController(
{
// ind 是啥意思?
index: cache.ind,
hash: cache.hash,
chunk: sendData,
},
fileStorageRoot
// 不要用 then
).then((res) => {
ctx.websocket.send(JSON.stringify(res));
});
Expand Down
1 change: 1 addition & 0 deletions apps/upload-file/tsconfig.build.tsbuildinfo

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions apps/upload-file/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"compilerOptions": {
"rootDir": "./",
"outDir": "./dist",
// 你都没写单测,没必要加 "vitest/globals"
"types": ["vitest/globals", "node"]
},
"include": ["src"]
Expand Down
2 changes: 2 additions & 0 deletions packages/backend/fs/fileBasicStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import {
readdir,
unlink,
rmdir,
// 这个 rename 没有用到
rename,
} from "fs/promises";

// 咋又从 fs 导入了?
import { PathLike, RmDirOptions, StatOptions } from "fs";
import { isValidString } from "@packages/common_utils";

Expand Down
3 changes: 3 additions & 0 deletions packages/backend/fs/fileSystmStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ export class FilePieceService {
* @param chunkIndex 可选参数,指定要检查的片段索引
* @returns 指定片段或合并后文件是否存在
*/
// 我感觉,代码跟训练营提供的样例项目就一模一样。。。。
// 建议自己做一遍吧,别抄代码
async isExist(chunkIndex?: number, filename?: string) {
const findByChunk = typeof chunkIndex === "number" && chunkIndex >= 0;
let name = "";
Expand All @@ -82,6 +84,7 @@ export class FilePieceService {
* @returns 返回文件数组
*/
async ls() {
// 连 fn2idx 这种箭头函数的名字都一模一样
const fn2idx = (filename: string) => path.basename(filename);
// 获取哈希值对应的目录下的所有文件
const pieces = await this._storage.ls(this.hashDir);
Expand Down
2 changes: 2 additions & 0 deletions packages/common/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@

// 文件目录要有讲究,源码应该放进 src 之类的目录,不要直接堆在根目录下。。。
// 检查给定的值是否为非空字符串。
export const isValidString = (filename: string) => {
return typeof filename === 'string' && filename.length > 0;
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
import indexDB from "./indexDB/index";
// 怎么又把产物推上去了
export { indexDB, };
1 change: 1 addition & 0 deletions packages/frontend/indexDB/index copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// constructor(DB_NAME: string) {
// this.DB_NAME = DB_NAME;
// }
// 这些 copy 文件是什么鬼

// // openDB
// public openStore = (
Expand Down
7 changes: 7 additions & 0 deletions packages/frontend/indexDB/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const SQL_NAME = "SQL_NAME";
const DB_NAME = "DB_NAME";

// Result 这个命名太随意了,没有指向性
interface Result {
name: string;
content: string;
Expand All @@ -20,6 +21,10 @@ interface Put {
let request: any;
let db: any;

// 其实这个文件适合做成单例 class,因为它是有状态的
// 你当前的设计要求外部按规则,先调用 init,之后才能调用其他数据操作接口
// 假如做成单例 class,就可以在 construct 里面自行做好数据初始化操作
// 外部只需直接操作数据即可
// 初始化数据库
export const init = () => {
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -54,6 +59,8 @@ export const init = () => {
// get操作,读取数据
export const get = (name: string): Promise<Result> => {
return new Promise<Result>((resolve, reject) => {
// 我感觉,这个类型规则,Get 和 Put 好像都没啥必要,外部并没有地方用到里面的 onsuccess 和 onerror 方法吧?
// 感觉有点过度设计了
const select: Get = db
.transaction([DB_NAME], "readonly")
.objectStore(DB_NAME)
Expand Down