单一文件上传[formData 格式]
主要核心
预览方式 bold 格式
const src = URL.createObjectURL(文件对象)
img.src = src
上传方式
const fd = new FormData()
fd.append('属性名', 值)
axios.post('/xx', fd)
单一文件上传(只适合图片)()[base64 格式]
主要核心
const fileReader = new fileReader()
fileReader.readAsDataURL(文件对象) // 读取文件对象转换为 base64格式
fileReader.onload(e => { // 读取文件是异步操作
console.log(e.target.result) // base 64格式
img.src = e.target.result
axios.post('/xx', e.target.result)
})
单一文件上传(自定义唯一文件名)
主要核心
自定义文件名核心(利用spark MD5包)
"spark-md5": "^3.0.1",
const fileReader = new fileReader()
fileReader.readAsArrayBuffer(文件对象)
fileReader.onload((e) => {
const spark = new sparkMD5.ArrayBuffer() // 读取buffer 格式数据
spark.append(e.target.result)
const HASH = spark.end() // 获取得到唯一hash 名文件
})
完整代码
;(function (){
const upload3 = document.querySelector('#upload3')
const up_file = upload3.querySelector('.upload_inp')
const uploadSer = upload3.querySelector('.upload_button.upload')
const upload_select1 = upload3.querySelector('.upload_button.select')
const upload_abbre = upload3.querySelector('.upload_abbre')
let _file = null // 文件对象
// 判断文件是否读取中
const isDisable = (e) => {
return e.classList.contains('loadig') || e.classList.contains('disable')
}
// 文件点击
function changeDisable(flag) {
if (flag) {
upload_select1.classList.add('disable')
uploadSer.classList.add('loading')
} else {
upload_select1.classList.remove('disable')
uploadSer.classList.remove('loading')
}
}
// 文件读取成base64格式
const getBase64 = (file) => {
return new Promise(reslove => {
const readFile = new FileReader()
readFile.readAsDataURL(file)
readFile.onload = (e) => {
reslove(e.target.result)
}
})
}
// 文件生成唯一hash 值
const changeBuffer = (file) => {
return new Promise(reslove => {
const readFile = new FileReader()
readFile.readAsArrayBuffer(file)
readFile.onload = (e) => {
const buffer = e.target.result
const spark = new SparkMD5.ArrayBuffer()
spark.append(buffer) // 开始 生成 唯一的hash
const HASH = spark.end() // 结束后拿到 hash
// console.log(file.name)
// console.log(HASH)
const suffix = /\.([A-Za-z0-9]+)$/.exec(file.name)[1] // 获取文件后缀名
reslove({
buffer,
suffix,
HASH,
filename: `${HASH}.${suffix}`
})
}
})
}
// 点击上传服务器
uploadSer.addEventListener('click', async function () {
// 判断是否有文件对象
if (!_file) return
const {filename} = await changeBuffer(_file) // 生成唯一 的hash 文件名
const fd = new FormData()
fd.append('file', _file)
fd.append('filename', filename)
try {
changeDisable(true)
const res = await instance.post('upload_single_name', fd)
if (res.code !== 0) throw res.codeText
alert('上传成功')
} catch (err) {
alert(err)
}
changeDisable(false)
_file = null
upload_abbre.style.display = 'none'
upload_abbre.children[0] = ''
})
// 监听文件状态
up_file.addEventListener('change', async function (e) {
const file = e.target.files
// 判断是否有文件
if (file.length <= 0) return alert('没有文件')
// 限制文件大小
// console.log(file[0])
if (file[0].size > 1024 * 1024 * 2) return alert('文件过大')
// 获取base64 格式字符串
changeDisable(true)
const res = await getBase64(file[0])
upload_abbre.style.display = 'block'
upload_abbre.children[0].src = res
changeDisable(false)
_file = file[0]
})
// 触发点击文件的操作
upload_select1.addEventListener('click', function() {
// 判断文件是否在读取中进行防抖操作
if(isDisable(this)) return
up_file.click()
})
})();
文件上传进度条显示
主要核心
axios 监听文件上传进度事件
onUploadProgress(e) { // 基于原生 XMLHttpRequest().upload.onprogress事件
e.loaded // 文件上传过程大小
e.total // 文件总大小
}
axios.post('/xx', data, {
onUploadProgress(e) {
e.loaded
e.total
}
})
完整代码
// 3. 监听 file 文件对象
up_file.addEventListener('change', async function(e) {
const target = e.target
// console.log(target.files)
const file = target.files[0]
// 3.1 判断是否有文件对象
if (file.length <= 0) return
// 3.2 判断文件类型
// console.log(file.type)
if (!/(PNG|JPG|JPEG)/i.test(file.type)) return alert('文件类型不对')
// 3.3 校验文件大小
if (file.size > 2 * 1024 * 1024) return alert('文件大小不超过2M')
// 上传 formate 格式
const fd = await changeFormData(file)
try {
const res = await instance.post('/upload_single', fd, {
onUploadProgress: (ev) => { // 利用onUploadProgress 监听事件
// ev.loaded 文件加载进度
// ev.total 文件总大小
upload_progress.style.display = 'block'
upload_value.style.width = `${ev.loaded / ev.total * 100}%`
}
})
upload_value.style.width = `100%`
await delay(1000)
alert('上传成功')
if (res.code !== 0) throw res.codeText
} catch (err) {
alert(err)
}
// 无论成功还是失败 将盒子隐藏
upload_progress.style.display = 'none'
upload_value.style.width = `0%`
})
多文件上传[文件进度管控]
核心思路
file.addEventListener('click', async function (){
const fileList = e.target.files
if(fileList.length <= 0) return
const list = fileList.map(item => {
return axios.post('/xx', data, {
onUploadProgress(e) {
// 做操作
}
})
})
try {
await Promise.all(list) // list 返回时promise 数组
} catch(err) {
}
// 无论成功还是失败 做一些操作
xxx
})
拖拽上传
核心思路
利用H5 新增事件
1. drogenter // 目标元素 进入区域
2. dorgleave // 目标元素 离开区域
3. dropover // 目标元素 经过区域
4. drog // 目标元素放入区域
注意:
1. 文件拖拽到浏览器默认是打开这个文件 需要阻止事件默认行为 e.preventDefault()
2. 通过 e.dataTransfer.files 拿到文件对象
代码
upload_drag.addEventListener('drop', function (e) {
e.preventDefault()
// console.log(e)
const file = e.dataTransfer.files[0] // 拿到文件对象
进行文件上传操作 ...
}
大文件切片上传
核心思路:
1. 文件change 事件触发 优先获取已经上传完成的切片,
2. 进行切片上传 利用文件对象.slice() 方法 进行切片
3.切片完成 发起合并切片请求
代码
// 根据文件大小获取切片数量和 每个切片大小
// file: 文件对象
const getSliceCount = (file) => {
let max = 100 * 1024 // 默认切片数量为 100kb
let count = Math.ceil(file.size / max) // file.size 获取b 向上取整
if (count > 100) { // 超过100 按 100 算
max = file.size / 100
count = 100
}
return {max, count}
}
// 拿到上传对象的文件HASH 名
const getHASH = (file) => {
return new Promise(reslove => {
const fileRead = new FileReader()
fileRead.readASArrayBuffer(file)
fileRead.onload = (e) => {
const spark = sparkMD5.ArrayBuffer()
spark.append(e.target.result)
const HASH = spark.end() // 获取文件唯一 hash名
const suffix = file.name.slice(file.name.indexOf('.'), ) // 获取文件后缀名 .jpg .png
reslove({
HASH,
suffix,
})
}
})
}
// 获取每一个切片文件的大小 组合
const chunkFile = (count, max, HASH, file, suffix) => {
// slice(0, 1024) 1
// slice(1024, 2048) 2
// slice(2048, xx)
const = chunkList = []
for(let i = 0; i < count; i++) {
chunkList.push({
file: file.slice(i*max, (i+1)*max)
HASH: `${HASH}_${i+1)${suffix)` // 文件名 xxx_1.jpg xxx_2.jpg
})
}
return chunkList
}
// 管控进度显示 index 从0 开始 的 执行到最后会比数量小1
const complect = (index, count, HASH) => {
if(index < count - 1) {
upload_value.style.width = `((index+1) / count)*100` + '%'
return
}
// 走到这一步 说明文件 切片 上传完成 (执行合并切片)
try {
const res = await instance.post('/upload_merge', {
HASH: HASH,
count
},{
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
if (res.code !== 0) throw res.codeText
alert('上传切片成功')
} catch (err) {
alert('合并切片失败')
}
}
部分核心代码
file_upload.addEventListener('change', async function (){
const file = e.target.files[0]
if(!file) return
// 拿到文件对象的HASH名
const { HASH, suffix} = await getHASH(file)
let readList = [] // 断点续传的文件
// 1. 优先获取已经上传完成的切片
try {
const res = await instance.get('/upload_already', {
params: {
HASH
}
if(res.code !== 0) throw xx
readList = res.fileList // res.fileList: 数组 包含缓存的文件名[xx_1.xx, xx_2.xx]
} catch(err) {
}
// 2. 切片上传文件
// 获取切片数量 大小
const = {max, count} = getSliceCount(file)
// 获取 切片组合
const list = chunkFile = (count, max, HASH, file, suffix)
list.forEach((item, index) => {
// 2.1 判断 缓存切片是否缓存
if(readList.length > 0 && readList.includes(item.HASH)) return complete(index, count, HASH)
const fd = new FormData()
fd.append('file', item.file)
fd.append('filename', item.filename)
try {
const res = await instance.post('/upload_chunk', fd)
if (res.code !== 0) throw '错误'
// 管控进度
compolete(index, count, HASH)
} catch (err) {
}
})
})
})
文件切片完整代码
// 大文件切片上传, 和断点续传
(function () {
const upload = document.querySelector('#upload7')
const upload_inp = upload.querySelector('.upload_inp')
const upload_select = upload.querySelector('.select')
const upload_progress = upload.querySelector('.upload_progress')
const upload_value = upload.querySelector('.value')
// 1. 点击上传图片
upload_select.addEventListener('click', function () {
upload_inp.click()
})
// 文件切片上传 max: 最大文件体积 count: 文件数量
const uploadFile = (file) => {
let max = 2 * 1024
let count = Math.ceil(file.size / max)
if (count >= 100) {
max = file.size / 100
count = 100
}
return {max, count}
}
// 将文件进行切片
const chunk = ({file, max, count, HASH, suffix}) => {
const chunk = []
for (let i = 0; i < count; i++) {
chunk.push({
file: file.slice(max * i, max * (i + 1)),
filename: `${HASH}_${i + 1}.${suffix}`
})
}
return chunk
}
// 拿到hash 值文件名
const getHASH = (file) => {
return new Promise(reslove => {
const fileReader = new FileReader()
fileReader.readAsArrayBuffer(file)
fileReader.onload = (e) => {
const spark = new SparkMD5.ArrayBuffer()
spark.append(e.target.result)
const HASH = spark.end()
const suffix = file.name.slice(file.name.indexOf('.'))
reslove({
HASH,
suffix,
file
})
}
})
}
// 进度管控
const compolete = async (i, count, HASH) => {
if (i < count - 1) {
upload_value.style.width = `${i / count * 100}%`
return
}
// 走到这一步 说明 文件上传完成 发送合并切片请求
upload_value.style.width = `100%`
try {
const res = await instance.post('/upload_merge', {
HASH: HASH,
count
},{
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
if (res.code !== 0) throw res.codeText
alert('上传切片成功')
} catch (err) {
alert('合并切片失败')
}
clear()
}
const clear = () => {
upload_progress.style.display = 'none'
upload_value.style.width = '0'
upload_select.classList.remove('loading')
}
// 2. 文件上传 change
upload_inp.addEventListener('change', async function (e) {
const file = e.target.files[0]
if (!file) return
upload_select.classList.add('loading')
let fileList = [] // 断点上传
upload_progress.style.display = 'block'
// 拿到文件对象的hash 名
const {HASH, suffix} = await getHASH(file)
// 2.1 优先获取已经上传的切片 fileList: []
try {
const res = await instance.get('/upload_already', {
params: {
HASH: HASH
}
})
if (res.code !== 0) throw res.codeText
console.log(res)
fileList = res.fileList
} catch (err) {
alert('获取切片失败')
}
// file 文件对象有一个方法 slice
const {max, count} = uploadFile(file)
// console.log(max, count)
// 拿到文件切片对象
const chunkArr = chunk({file, max, suffix, HASH, count})
// return
console.log(chunkArr)
console.log(fileList)
chunkArr.forEach(async (item, index) => {
// 判断是否是断点上传
if (fileList.length > 0 && fileList.includes(item.filename)) return compolete(index, count, HASH)
const fd = new FormData()
fd.append('file', item.file)
fd.append('filename', item.filename)
try {
const res = await instance.post('/upload_chunk', fd)
if (res.code !== 0) throw '错误'
// 管控进度
compolete(index, count, HASH)
} catch (err) {
clear()
}
})
})
})()