赞
踩
大家好我是刷子哥,今天学了下大文件上传马上把笔记分享出来大家一起学习;
夫学须志也,才须学也,非学无以广才,非志无以成学(要知真知必须使身心在宁静中研究探讨,人们的才能是从不断学习中积累起来的,不学习就难以增长才干,不立志就难以学有所成)。——诸葛亮 ,废话不说直接干货。
首先这个大文件上传的思路就是前端把 file用slice进行切割将文件切割成指定的等份,while 循环调用上传的接口向后端发送数据,直到切割文件的大小和文件的大小一致就终止循环。后端有文件就进行叠加文件,没有就新建一个文件。
这次我们在根目录新建两个文件夹一个是后端一个是前端的代码。
npm i init -y
然后这是前端的依赖{
"name": "uploadr",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "vite"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^1.1.3",
"vite": "^3.2.2"
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传文件</title>
</head>
<body>
<ul>
<li>
<progress value="0" id="uploadProgress"> </progress>
</li>
<li>
<input type="file" id="fileUpload" value="请选则视频"/>
</li>
<li>
<span id="uploadInfo">你都上传了那些文件</span>
</li>
<li>
<button id="uploadBtn">点击上传</button>
</li>
</ul>
<!-- SyntaxError: Cannot use import statement outside a module (at 加上type='module'-->
<script type="module" src="./index.js"></script>
</body>
</html>
// 字典
export const UPLOADTYPE = {
'video/mp4':'mp4',
'video/mp3':'mp3'
}
export const UPLOADINFO = {
'NO_INFO':'请你您选择文件进行上传',
'TYPE_INFO':'您上传的文件格式不正确,仅支持mp4,mp3',
'UPLOAD_LOADING':'上传中',
'UPLOAD_ERROR':'上传失败了请您检查网络',
'UPLOAD_SUCCESS':'上传成功'
}
// 路径
export const BASEURL = 'http://localhost:8000/file/upload'
// 每次切多少
export const CHUNKSIZE = 64 * 1024
import {UPLOADINFO, UPLOADTYPE, BASEURL, CHUNKSIZE} from './cofig'
import axios from "axios";
;((dcu) => {
let uploadBtn = dcu.querySelector('#uploadBtn');
let uploadProgress = dcu.querySelector('#uploadProgress');
let fileUpload = dcu.querySelector('#fileUpload');
let uploadInfo = dcu.querySelector('#uploadInfo');
let uploadSize = 0 //用于保存当前上传了多少
const init = () => {
bindEvent()
}
function bindEvent() {
uploadBtn.addEventListener('click', uploadHandler, false)
}
async function uploadHandler() {
// let file = fileUpload.files[0]
const {files: [file]} = fileUpload
console.log(file)
if (!file) { // 没有文件
uploadInfo.innerText = UPLOADINFO['NO_INFO']
return;
}
if (!UPLOADTYPE[file.type]) { // 规定上传的类型
uploadInfo.innerText = UPLOADINFO['TYPE_INFO']
return
}
const {type, name, size} = file
let fileName = new Date().getTime() + '_' + name
uploadProgress.max = size
uploadInfo.innerText = uploadInfo.innerText = UPLOADINFO['UPLOAD_LOADING']
let res; // 返回成功的结果
while (uploadSize < size) { // 规定的文件小于要上传的文件就切片处理
let chunk = file.slice(uploadSize, uploadSize + CHUNKSIZE)
const formData = createFormData({
name: name,
type: type,
fileName: fileName,
size: size,
uploadSize: uploadSize,
file: chunk
})
console.log('idwa', formData) // 上传的fromData对象
try {
res = await axios.post(BASEURL, formData) // 上传的接口
console.log('dwa', res)
} catch (err) {
uploadInfo.innerText = uploadInfo.innerText = `${UPLOADINFO['UPLOAD_ERROR']} + ${err}`
throw err
}
// 拼接上传的数据
uploadSize += chunk.size // 拼接切割的size
uploadProgress.value = uploadSize // 进度条的zize
}
uploadInfo.innerText = uploadInfo.innerText = UPLOADINFO['UPLOAD_SUCCESS']
// 上传完成创建视频标签
if(res.data.fileUrl) await createVideo(res.data.fileUrl) // 上传成功创建视频标签
}
function createFormData({name, type, fileName, size, uploadSize, file}) { // 创建formData对象
let fd = new FormData()
fd.append('name', name)
fd.append('type', type)
fd.append('fileName', fileName)
fd.append('size', size)
fd.append('uploadSize', uploadSize)
fd.append('file', file)
return fd
}
function createVideo (url) { //创建一个视频的标签插入到页面
console.log(url)
const eleVideo = document.createElement('video')
eleVideo.controls = true
eleVideo.width = '500'
eleVideo.src = url
document.body.appendChild(eleVideo)
}
// 还有判断 size 大小的逻辑 以及上传中按钮隐藏... 大家自己补充吧
init()
})(document);
后端主要用到了nodejs、express、express-fileupload、 fs、 path、
初始化后端的项目npm i init -y
然后这是前端的依赖
{
"name": "serverupload",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev ": "nodemon ./app.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.18.2",
"express-fileupload": "^1.4.0",
"nodemon": "^2.0.20"
}
}
const filePath = resolve(__dirname, './uploadTemp/' + nameFile)
这个就是文件的操作,去找到同级别的 uploadTemp文件,上传的视频都会到 uploadTemp 文件夹。
Fs
fs.existsSync() 方法用于同步检查给定路径中是否已存在文件。它返回一个布尔值,该值指示文件的存在。
fs.appendFileSync() 方法的用途是通过异步的方法将文本内容或数据添加到文件里,如果文件不存在则会自动创建
fs.writeFileSync() 方法用于将数据同步写入文件。如果该文件已经存在,则替换该文件。 “ options”参数可用于修改方法的函数。
Path
path.extname()
方法返回 path
的扩展名,即 path
的最后一部分中从最后一次出现的 .
(句点)字符到字符串的结尾。 如果 path
的最后一部分中没有 .
,或者除了 path
的基本名称(参见 path.basename()
)的第一个字符之外没有 .
个字符,则返回空字符串。
path.resolve() 方法会把一个路径或路径片段的序列解析为一个绝对路径。
给定的路径的序列是从右往左被处理的,后面每个 path 被依次解析,直到构造完成一个绝对路径。 例如,给定的路径片段的序列为:/foo、/bar、baz,则调用 path.resolve(‘/foo’, ‘/bar’, ‘baz’) 会返回 /bar/baz。
app.use('/',express.static('uploadTemp'))
当前端访问路径http://localhost:8000/1111.mp4 的时候就像当于uploadTemp文件夹下面的 1111.mp4 视频。
在node.js 中 let { file } = req.files
通过 req.files 才能拿到前端传入的 file 对象。
app.use(bodyParser.urlencoded({extended:true}))
这是常用的方法,常见的前端请求解决方案如表单post提交、axios、fetch等库的post请求都需要这个中间件进行解析,返回json的格式数据。当请求的数据类型是application/x-www-form-urlencoded时才会进入这个中间件进行处理。
解析URL-encode数据的方法,true的话使用qs库来解析,false的话使用querystring库去解决, "extended": true,
app.use(bodyParser.json())
解析并返回 json格式的数据,这是常用的方法。内部会查看content-type,只有是正确的content-type默认是application/json才进入这个中间件解析处理。
const express = require('express')
const bodyParser = require('body-parser')
const fileupload = require('express-fileupload')
const app = express()
const {extname,resolve} = require('path')
const {existsSync,appendFileSync,writeFileSync} = require('fs')
app.all('*',(req,res,next) => {
res.header('Access-Control-Allow-origin','*')
res.header('Access-Control-Allow-Methods','POST,GET')
next() // 执行中间件
}) // 解决跨域问题
app.use(bodyParser.urlencoded({extended:true}))
app.use(fileupload()) // 使用插件
app.use(bodyParser.json())
app.use('/',express.static('uploadTemp'))
app.post('/file/upload',(req,res) => {
const UPLOADTYPE = {
'video/mp4':'mp4',
'video/mp3':'mp3'
}
console.log(req)
let {name,type,fileName,size,uploadSize} = req.body
let { file } = req.files
if(!file) {
res.send({
code:101,
msg:'NO file upload'
})
return
}
if(!UPLOADTYPE[type]) {
res.send({
code:102,
msg:'the type is not allowed for uploading.'
})
return
}
let nameFile = fileName + extname(name) // path 模块取文件的后缀名
const filePath = resolve(__dirname, './uploadTemp/' + nameFile)
console.log(name,file,nameFile)
if(uploadSize !== '0' ) { // 有文件大小进行文件追加操作
if(!existsSync(filePath)) {
res.send({
msg:"no file exists",
code:1003
})
return;
}
appendFileSync(filePath,file.data)
res.send({
msg:"appended",
code:0,
fileUrl:'http://localhost:8000/' + nameFile
}) // 返回前端可以访问视频的地址
return;
}
writeFileSync(filePath,file.data) // 写入文件
res.send({
msg:"file is create",
code:0
})
})
const PORT = 8000
app.listen(PORT , () => {
console.log('启动了',PORT)
}) // 监听端口号
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。