首页 > 其他分享 >前端大文件上传如何做到刷新续传?

前端大文件上传如何做到刷新续传?

时间:2023-10-11 15:48:40浏览次数:27  
标签:const socket userId io 刷新 console 上传 续传

前言

这两天在学习阿里云oss上传。踩了不少坑,终于实现了大文件分片、断点续传的功能。这篇文章主要分享学习笔记,希望能给大家一些帮助。先看效果

 

技术栈

1.前端: react+Ts + axios 上传文件

2.Node部分:定义接口、阿里云 oss

3.socket.io :实时同步上传进度 特别说明 axios 中 onUploadProgress 只是上传本地文件的进度,不是上传服务器存入的进度,需要socket.io 从服务端实时返回上传进度

环境安装

需进行阿里云oss配置,获取appid、密钥等参数 ↓阿里云登录 - 欢迎登录阿里云,安全稳定的云计算服务平台 以下是创建node服务所需依赖包

// 下载 Koa 模块
npm install koa
// 下载 Koa Router 模块 https://www.npmjs.com/package/koa-Router[包含示例代码]
npm install koa-router
// 下载 @koa/cors 模块
npm install @koa/cors
// 下载 ali-oss 模块
npm install ali-oss
// 下载 koa-body 模块
npm install koa-body
// 下载 socket.io
npm install socket.io
前端部分

前端使用react+Ts,但无论哪种框架,其实业务逻辑是一样的

初始化socket

let userId = localStorage.getItem('userId');
if (!userId) {
userId = new Date().getTime() + '';
localStorage.setItem('userId', userId);
}
let host = 'http://127.0.0.1:3000'

const soket = io(host);
soket.on('connect', function(){
console.log('链接了 Connected to server');
});
// 模拟用户登录
soket.emit('login',{
userId
})

soket.on('success', data => {
console.log('success',data)
})

文件上传

const upload = async () => {
// FileList 内置接口
const file = (inputRef.current?.files as FileList)[0];
console.log('inputRef', file);
if (!file) {
message.error('没有选择文件');
return;
}
let formData = new FormData();
formData.append('file', file);
let userId = localStorage.getItem('userId') as string
formData.append('userId',userId)
await axios.post(host+'/upload', formData, {
// onUploadProgress 监听的是客户端发送数据的进度,而不是存储服务器的进度。
onUploadProgress: (progressEvent: any) => {
const percentage = Math.round((progressEvent.loaded * 100) / progressEvent.total);
console.log(`Upload progress: ${percentage}%`, progressEvent);
}
});
};

 

进度回显

const [progress,setProgress] = useState<number>(0)
soket.on('uploadding', data => {
console.log('uploadding',data)
setProgress(data)
})

node部分

后台使用koa创建node服务,主要分为api接口、阿里云业务函数、socket.io 实时发送上传进度

socket.io

const { createServer } = require("http"); // 导出创建服务的模块函数
const { Server } = require("socket.io"); // 创建socket.io 服务的函数
const httpServer = createServer(app.callback()); // 创建一个http服务实例,app.callback() 作为服务器的请求处理函数
const io = new Server(httpServer, {
cors: {
origin: "*" // 配置socket允许跨越
}
});
上传接口

// 上传接口
router.post('/upload', async (ctx, next) => {
let file = ctx.request.files.file;
// 用户id
let userId = ctx.request.body.userId
try {
let result = null;
await next();
// 判断文件大小,超过partSize进行分片上传
if (file.size < partSize) {
console.log('直连操作');
result = await commonUpload(file, userId);
} else {
console.log('分片上传');
result = await multipartUpload(file, userId);
}
ctx.body = {
code: 200,
message: 'success',
data: result
};
} catch (error) {
console.log('error', error);
ctx.body = {
message: '上传失败',
code: 401
};
}
});

暂停接口

router.post('/break', async (ctx) => {

let userId = ctx.request.body.userId
// 取出 当前用户的阿里云实例,用于仅关闭当前上传
let itemClient = userList[userId]['client']
if (itemClient) {
itemClient.cancel();
ctx.body = {
code: 200,
message: "暂停成功"
}
} else {
ctx.body = {
code: 401,
message: "暂停失败"
}
}

});


继续上传接口

// 继续上传
router.post('/continue', async (ctx) => {
let userId = ctx.request.body.userId
ctx.body = {
code: 200,
message: '已继续上传',

};
try {
resumeMultiparUpload(userId)
} catch (error) {
console.log('继续上传报错')
}

});

分片上传

// 分片上传
const multipartUpload = async (file, userId) => {

try {
let result = await userList[userId].client.multipartUpload(file.originalFilename, file.filepath, {
parallel,
partSize,
progress: (p, checkpoint) => {
onProgress(p, checkpoint, userId)
}
});
return result;
} catch (error) {
console.log('multipartUpload', error)
}
};


断点续传

// 断点续传
const resumeMultiparUpload = async (userId) => {
// 获取当前用户分片缓存
try {
let checkpoint = checkpoints[userId]
const { uploadId, file } = checkpoint;
let result = await userList[userId].client.multipartUpload(uploadId, file, {
parallel,// 分片数量
partSize,//分片大小
progress: (p, checkpoint) => {
onProgress(p, checkpoint, userId)
},// 上传进度回调函数
checkpoint // 断点续传缓存目录
});
//上传后,删除切片数据
delete checkpoints[userId]
console.log('result', result)
return result;
} catch (error) {
console.log('error 获取当前用户分片缓存')
}

}


进度回显

// 上传进度
const onProgress = async (p, checkpoint, userId) => { // p 进度,checkpoint 当前分片上传数据
let step = Math.floor(p * 100); // 转换为百分比
io.to(userList[userId].socketId).emit('uploadding', step) // 发给当前客户端
// io.emit('uploadding',step) 群发
// 存储分片数据,用户续传
console.log('上传进度', step)
checkpoints[userId] = checkpoint
};


socket.io私聊

const userList = {} // 用户数据
const partSize = 1024 * 100; // 每个分片大小(byte) 100kb
const parallel = 3; // 同时上传的分片数
let checkpoints = {}; // 记录上传分片数据,用于断点续传
// oss客户端实例

// socket.io系统事件,监听链接状态
io.on("connection", (socket) => {
// 监听客户端信息数据,存储用户信息

socket.on('login', (data) => {
// 用户未链接oss,进行创建oss实例
if (!userList[data.userId]) {

let client = new OSS({
// yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
region: 'oss-cn-beijing',
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控W制台创建RAM用户。
accessKeyId: 'xxx',
accessKeySecret: 'xx',
bucket: 'xxx
});

// 将socket.id 与用户信息关联存储,方便私聊发送
userList[data.userId] = {
...data,
socketId: socket.id,
client: client
}
console.log('socket.id', socket.id)
console.log('获取到用户数据')
} else { // 已链接oss,进行只更新socket.id
userList[data.userId].socketId = socket.id
}
})
socket.emit('success', '服务端链接成功了')
// socket.io 系统事件-客户端断开监听
socket.on('disconnect', () => {
console.log('客户端断开了')
// io.emit('quit', socket.id)
})
});
纯前端操作部分如无服务端业务要求,建议客户端调用阿里云sdk,实现上传oss功能,操作如node一致,阿里云sdk同样支持分片上传等,最方便的是无需再创建socket获取进度。 兄弟们,点赞收藏过20,下篇文章更新呀

Socket 相关api

在Socket.IO 中,客户端和服务端都有一些系统事件。

服务端系统事件:

connection:当客户端与服务器建立连接时触发。可以在此事件中执行与连接相关的操作。

io.on("connection", (socket) => {
// 处理连接事件
});
disconnect:当客户端与服务器断开连接时触发。可以在此事件中执行与断开连接相关的操作。

socket.on("disconnect", () => {
// 处理断开连接事件
});
error:当在连接过程中发生错误时触发。可以在此事件中处理连接错误。

socket.on("error", (err) => {
// 处理连接错误事件
});
to 在 Socket.IO 中,to 方法用于向特定房间或客户端发送消息。它允许你将消息发送给指定的接收者。to 方法的使用方法如下:

io.on('connection', (socket) => {

// 建议 将socket.id 与用户信息关联存储,方便私聊发送
// 向指定客户端发送消息
io.to(socket.id).emit('message', 'Hello from server!');
});
使用 io.to(socket.id).emit('message', 'Hello from server!') 向特定客户端发送消息,其中 socket.id 表示当前客户端的唯一标识符。

客户端系统事件:

connect:当客户端成功连接到服务器时触发。该事件仅发生在客户端连接成功时。

socket.on("connect", () => {
// 处理连接成功事件
});
disconnect:当客户端与服务器断开连接时触发。可以在此事件中执行与断开连接相关的操作。

socket.on("disconnect", () => {
// 处理断开连接事件
});
error:当在连接过程中发生错误时触发。可以在此事件中处理连接错误。

socket.on("error", (err) => {
// 处理连接错误事件
});
注意socket 开启跨越 :::warning http://socket.io 需配置跨越,否则无法链接,参考 https://socket.io/zh-CN/docs/v4/handling-cors/ :::

const io = new Server(httpServer, {
cors: {
origin: "http://localhost:8080" // 前端访问地址 、"*" 允许所有跨越
}
});

httpServer.listen(3000);
koa+socket使用事项 :::warning 接口后台和socket端口一致情况下,需使用包含socket的服务实例来创建监听,否则socket无法链接 ::: 在koa中使用socket.io 需要创建一个包含socket.io的服务实例,代码示例如下:

const app = new (require("koa"))();
const router = require("koa-Router")();
const { createServer } = require("http");
const { koaBody } = require("koa-body");
const {Server} = require('socket.io')
app.use(cors()); // 允许接口跨域
app.use(router.routes());
// 创建socket服务
const httpServer = createServer(app.callback());
const io = new Server(httpServer,{
cors:{
origin:"*" // 允许socket跨域
}
})
io.on('connection', () => {
console.log('服务链接了')
/* … */ });
// 使用包含socket的服务示例,如果使用koa中的app实例,则无法监听socket服务
httpServer.listen(9000, () => {
console.log("koa server listening on port 9000");
});

 

参考文章:http://blog.ncmem.com/wordpress/2023/10/11/%e5%89%8d%e7%ab%af%e5%a4%a7%e6%96%87%e4%bb%b6%e4%b8%8a%e4%bc%a0%e5%a6%82%e4%bd%95%e5%81%9a%e5%88%b0%e5%88%b7%e6%96%b0%e7%bb%ad%e4%bc%a0%ef%bc%9f/

欢迎入群一起讨论

 

标签:const,socket,userId,io,刷新,console,上传,续传
From: https://www.cnblogs.com/songsu/p/17757320.html

相关文章

  • 监听上传的服务器文件是否改变,从而刷新页面
     监听上传的服务器文件是否改变,从而刷新页面=>interfaceOptions{timer?:number;}classUpdater{oldScript:string[];//存储第一次值也就是script的hash信息newScript:string[];//获取新的值也就是新的script的hash信息dispatch:Record<string,Fun......
  • 文件上传ssh 并显示进度条
    importosimportparamikoimporttimedefdownload_file_with_progress(hostname,port,username,password,remote_path,local_path):ssh=paramiko.SSHClient()ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())ssh.connect(hostname=hostname,p......
  • 刷新iframe
    对于iframe(iframeMain是iframe的ID)document.all.iframeMain.src="XXX";//显示iframe中最后一次刷新得到的内容document.all.iframeMain.contentWindow.location="XXX";//重新刷新iframe的内容firefox下没有document.all对象要用document.getEle......
  • java如何做大体积的文件上传和下载
    在Java中,实现大体积文件的上传和下载涉及到处理文件的分片、并发上传、断点续传等问题。本文将详细介绍如何通过Java实现大体积文件的上传和下载。1.文件上传文件上传是将本地文件上传到服务器的过程。对于大体积文件的上传,我们可以将文件分成多个小片段进行并发上传。1.1文件......
  • 如何优雅的上传博客
    平常使用markdown(Typora)写文档很方便,在各式各样的平台展示都不成问题。但是图片上传博客平台却很不方便,上传git平台另说。这里介绍一个本人修改的博客图片上传工具,xle97/dotnet-cnblogs......
  • 前端如何实现大文件上传
    在开发过程中,经常会遇到一些较大文件上传,如果只使用一次请求去上传文件,一旦这次请求中出现什么问题,那么无论这次上传了多少文件,都会失去效果,用户则需要重新上传所有资源。所以就想到一种方式,将一个大文件分成多个小文件,这样通过多个请求实现大文件上传。接下来我们就来看看具体......
  • git上传至公共或私有github
    1.下载gitbash参考链接:https://git-scm.com/download2.创建git的秘钥gitconfig--globaluser.name"githubname"gitconfig--globaluser.email"githubemail"ssh-keygen-trsa-C"githubemail"其中:githubname是你的名称,githubemail是你的邮箱3.添加de......
  • Selenium借助AutoIt完成文件的上传与下载
    文件上传1,编辑首先提前下载好AutoIT,先了解https://blog.csdn.net/weixin_39218743/article/details/87808776手上没有带上传文件的网址,先用百度的上传照片吧!打开AutoIT工具组件中的脚本编辑器sciTEScriptEditorWinWaitActive("打开")Send('D:\img\11.jpg')Sleep(2000)Send("{......
  • 前端大文件上传方法
    最近遇见一个需要上传百兆大文件的需求,调研了七牛和腾讯云的切片分段上传功能,因此在此整理前端大文件上传相关功能的实现。在某些业务中,大文件上传是一个比较重要的交互场景,如上传入库比较大的Excel表格数据、上传影音文件等。如果文件体积比较大,或者网络条件不好时,上传的时间会......
  • vue实现视频上传功能
    本文实例为大家分享了vue实现视频上传功能的具体代码,供大家参考,具体内容如下环境:vue+TS上传视频+上传到阿里云主要处理前端在vue下上传视频使用的是阿里云的视频点播服务1、需要后台去申请一个开发API,请求阿里云的接口访问控制2、有了开发视频的token,供给前端3、前端去请......