文章已同步至掘金:https://juejin.cn/post/6925341165000589326
欢迎访问😃,有任何问题都可留言评论哦~
FileReader 对象可以一步读取文件内容,使用 File 对象或 Blob 对象指定要读取的文件或数据。
File 对象:可以是 input 元素上选择文件后返回的 FileList 对象,也可以是来自拖放操作生成的 DataTransfer 对象, 还可以是来自在一个 HTMLCanvasElement 上执行 mozGetAsFile 方法后返回的结果。
Blob 对象:表示一个不可变、原始数据的类文件对象,Blob 表示的不一定是JavaScript原生格式的数据。File 接口继承自 Blob 对象,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。Blob.size 表示包含数据大小(字节),Blob.type 表示该 Blob 对象的 Mime 类型。
方法
FileReader接口有4个方法,其中3个用来读取文件,另一个用来中断读取。
无论读取成功或失败,方法并不会返回读取结果,这一结果存储在result属性中。
方法名 | 参数 | 描述 |
---|---|---|
readAsBinaryString | file | 将文件读取为二进制编码,常搭配xlsx 库来处理excel文件 |
readAsText | file,[encoding] | 将文件读取为文本(.csv、.txt),一般会指定utf-8编码 |
readAsDataURL | file | 将文件读取为DataURL,URL格式的Base64字符串,常用来读取本地图片并展示 |
abort | (none) | 终端读取操作 |
属性
-
FileReader.error :表示在读取文件时发生的错误 。
-
FileReader.readyState:表示
FileReader
状态的数字。取值如下:
常量名 | 值 | 描述 |
---|---|---|
EMPTY |
0 |
还没有加载任何数据. |
LOADING |
1 |
数据正在被加载. |
DONE |
2 |
已完成全部的读取请求. |
- FileReader.result:文件的内容。该属性仅在读取操作完成后才有效,数据的格式取决于使用哪个方法来启动读取操作。
事件处理程序
事件 | 调用时机 |
---|---|
onabort | 当读取操作被中止时调用 |
onerror | 当读取操作发生错误时调用 |
onload | 当读取操作成功完成时调用 |
onloadend | 当读取操作完成时调用,不管是成功还是失败.该处理程序在onload 或者onerror 之后调用 |
onloadstart | 当读取操作将要开始之前调用 |
onprogress | 在读取数据过程中周期性调用 |
input-accept属性
accept 属性规定了可通过文件上传提交的服务器接受的文件类型。
注意: accept 属性仅适用于 <input type="file">
。
加上
multiple
可以多选文件,这里暂时不实现多选
语法:
<input accept="audio/*|video/*|image/*|MIME_type">
提示: 如需规定多个值,请使用逗号分隔(比如 <input accept="audio/*,video/*,image/*" />
)。
属性值:
值 | 描述 |
---|---|
audio/* | 接受所有的声音文件。 |
video/* | 接受所有的视频文件。 |
image/* | 接受所有的图像文件。 |
MIME_type | 一个有效的 MIME 类型,不带参数。请参阅 IANA MIME 类型,获得标准 MIME 类型的完整列表。 |
常用MIME类型:
后缀名 MIME名称
*.3gpp audio/3gpp, video/3gpp
*.ac3 audio/ac3
*.asf allpication/vnd.ms-asf
*.au audio/basic
*.css text/css
*.csv text/csv
*.doc application/msword
*.dot application/msword
*.dtd application/xml-dtd
*.dwg image/vnd.dwg
*.dxf image/vnd.dxf
*.gif image/gif
*.htm text/html
*.html text/html
*.jp2 image/jp2
*.jpe image/jpeg
*.jpeg image/jpeg
*.jpg image/jpeg
*.js text/javascript, application/javascript
*.json application/json
*.mp2 audio/mpeg, video/mpeg
*.mp3 audio/mpeg
*.mp4 audio/mp4, video/mp4
*.mpeg video/mpeg
*.mpg video/mpeg
*.mpp application/vnd.ms-project
*.ogg application/ogg, audio/ogg
*.pdf application/pdf
*.png image/png
*.pot application/vnd.ms-powerpoint
*.pps application/vnd.ms-powerpoint
*.ppt application/vnd.ms-powerpoint
*.rtf application/rtf, text/rtf
*.svf image/vnd.svf
*.tif image/tiff
*.tiff image/tiff
*.txt text/plain
*.wdb application/vnd.ms-works
*.wps application/vnd.ms-works
*.xhtml application/xhtml+xml
*.xlc application/vnd.ms-excel
*.xlm application/vnd.ms-excel
*.xls application/vnd.ms-excel
*.xlt application/vnd.ms-excel
*.xlw application/vnd.ms-excel
*.xml text/xml, application/xml
*.zip aplication/zip
*.xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
示例
前端代码
<template>
<div>
<!-- 上传单文件.csv || .txt || .xlsx -->
<el-button type="primary"
@click="$refs.uploadInput.click()">上传单文件
<i class="el-icon-upload el-icon--right"></i>
</el-button>
<input type="file" style="display: none" ref="uploadInput" @change="importFile" accept=".csv, .txt, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" />
<div ref="showFile"></div>
<hr>
{{fileData}}
<hr>
<!-- 上传多文件.csv || .txt || .xlsx -->
<el-button type="primary"
@click="$refs.uploadInputs.click()">上传多文件
<i class="el-icon-upload el-icon--right"></i>
</el-button>
<input type="file" style="display: none" ref="uploadInputs" @change="importFiles" multiple accept=".csv, .txt, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" />
<div ref="showFile"></div>
<hr>
{{fileData}}
<hr>
<!-- 上传图片 .jpg || .jpeg || .png -->
<span class="show-span">
<span class="span" v-for="item in imgList" :key="item.imgName">
<img :src="item.url" class="img" alt="">
<i class="el-icon-error icon" @click="deleteImg(item.imgName)"></i>
</span>
</span>
<span @click="$refs.uploadImg.click()" class="img-span">
<span class="img-inside1 inside"></span>
<span class="img-inside2 inside"></span>
</span>
<input type="file" style="display: none" ref="uploadImg" @change="importImg" accept="image/*" />
</div>
</template>
<script>
import XLSX from 'xlsx'
import $ from 'jquery'
export default {
name: 'App',
data () {
return {
fileData: '',
imgList: []
}
},
methods: {
importImg (event) {
let files = event.target.files
if (!files.length) {
return;
}
let file = files[0];
if (!/(.png|.jpeg|.jpg)$/.test(file.name)) {
this.$message('上传文件格式错误')
return
}
let reader = new FileReader();
reader.readAsDataURL(file)
reader.onload = () => {
let base64 = reader.result
$.ajax({
url: "http://www.fanjunyang.xyz:9675/uploadImg",
data: {base64},
type: "POST",
dataType: "json",
success: data => {
const {imgName, url} = data.data
this.imgList = [{imgName ,url}, ...this.imgList]
}
})
}
this.$refs.uploadImg.value = ''
},
deleteImg (imgName) {
if (!imgName) {
return
}
this.$confirm('确定删除该图片吗?', '提示').then(() => {
$.ajax({
url: "http://www.fanjunyang.xyz:9675/deleteImg",
data: { imgName },
type: "POST",
dataType: "json",
success: data => {
if (!data.code) {
this.imgList.splice(this.imgList.findIndex(img => img.imgName === imgName), 1)
this.$message.success('删除成功')
}
}
})
}).catch((e) => {
console.log(e);
})
},
importFiles (event) {
let files = event.target.files
console.log(files)
},
importFile (event) {
let files = event.target.files
if (!files.length) {
return;
}
let file = files[0];
if (!/(.csv|.xlsx|.txt)$/.test(file.name)) {
this.$message('上传文件格式错误')
return
}
let reader = new FileReader();
if (/(.csv|.txt)$/.test(file.name)) {
reader.readAsText (file, 'utf-8')
reader.onload = () => {
// 数据
let result = reader.result
if (/.txt$/.test(file.name)) {
// 最终数据
// console.log(result);
this.fileData = result
}
// 处理CSV
if (/.csv$/.test(file.name)) {
let arr = result.split('\n')
let arrHeader = arr[0].split(',').map(item => item.trim())
let csvResult = arr.map(item => {
let _str = {}
item.split(',').forEach((v, i) => {
_str[arrHeader[i]] = v.trim()
})
return _str
})
csvResult.shift()
csvResult.pop()
// 最终数据
// console.log(csvResult)
this.fileData = csvResult
}
};
}
if (/.xlsx$/.test(file.name)) {
reader.readAsBinaryString(file)
reader.onload = (event) => {
try {
let data = event.target.result
let workBook = XLSX.read(data, {type: 'binary'}) // 以二进制流方式读取得到整份excel表格对象
// console.log(workBook.SheetNames) // 获取各个sheet表,可拿到自己需要的表
let result = {} // 存储数据
workBook.SheetNames.forEach(sheetName => {
let workSheet = workBook.Sheets[sheetName]
result[sheetName] = XLSX.utils.sheet_to_json(workSheet)
// console.log(workBook.Sheets[sheetName]['!ref']) // 表头
})
// 最终数据
// console.log(JSON.stringify(result, null, 2))
this.fileData = result
} catch (e) {
console.log(e);
return
}
};
}
reader.onerror = () => {
this.$message.error('文件读取失败')
this.fileData = ''
}
this.$refs.showFile.innerHTML = `<i class="el-icon-document"></i>${file.name}`
this.$refs.uploadInput.value = ''
},
}
}
</script>
<style scoped>
.icon {
position: absolute;
right: 0px;
top: -10px;
color: red;
font-size: 25px;
}
.span {
display: inline-block;
position: relative;
}
.img {
width: 200px;
height: 200px;
margin-right: 10px;
}
.img-span {
display: inline-block;
width: 200px;
height: 200px;
border-radius: 5px;
border: 1px dashed #ccc;
position: relative;
}
.img-inside1 {
width: 50px;
height: 2px;
display: inline-block;
background-color: #ccc;
position: absolute;
top: 50%;
left: 50%;
margin-left: -25px;
margin-top: -1px;
}
.img-inside2 {
width: 2px;
height: 50px;
display: inline-block;
background-color: #ccc;
position: absolute;
top: 50%;
left: 50%;
margin-left: -1px;
margin-top: -25px;
}
.img-span:hover {
border: 1px dashed aqua;
}
.img-span:hover .inside {
background-color: aqua;
}
</style>
后端代码
const Koa = require("koa")
const route = require('koa-route');
const fs = require('fs')
const Path = require('path')
const cors = require('koa-cors')
const bodyparser = require('koa-bodyparser')
const { nanoid } = require('nanoid');
const app = new Koa();
const { PORT, HOST} = require('./config');
app.use(cors())
app.use(bodyparser())
// 测试接口
const test = ctx => {
ctx.body = "测试接口"
}
// 上传图片
const uploadImg = async ctx => {
const { base64 } = ctx.request.body
if (!base64) {
ctx.body = {
code: 1,
msg: '参数错误'
}
return
}
const base64Data = base64.replace(/^data:image\/\w+;base64,/, "");
const dataBuffer = Buffer.from(base64Data, 'base64');
const imgName = `${nanoid()}.${/^data:image\/(\w+);base64,/.exec(base64)[1]}`
const path = Path.resolve(__dirname, './images')
try {
const result = await new Promise((resolve,rejects) => {
fs.writeFile(`${path}/${imgName}`, dataBuffer, (err) => {
if(err){
rejects({
code: 1,
msg: err
})
}else{
resolve({
code: 0,
data: {
url: HOST + imgName,
imgName
}
})
}
});
})
ctx.body = result
} catch(e) {
console.log(`upload:${e}`);
}
}
// 删除图片
const deleteImg = async ctx => {
const { imgName } = ctx.request.body
if (!imgName) {
ctx.body = {
code: 1,
msg: '参数错误'
}
return
}
const path = Path.join(__dirname, `images/${imgName}`)
try {
let result = ''
if (fs.existsSync(path)) {
result = await new Promise((resolve, rejects) => {
fs.unlink(path, err => {
if (err) {
rejects({
code: 1,
msg: err
})
} else {
resolve({
code: 0,
msg: '删除成功'
})
}
})
})
} else {
result = {
code: 1,
msg: '找不到指定的文件'
}
}
ctx.body = result
} catch(e) {
console.log(`delete:${e}`);
}
}
// 注册路由
app.use(route.get('/test', test))
app.use(route.post('/uploadImg', uploadImg))
app.use(route.post('/deleteImg', deleteImg))
app.listen(PORT, () => {
console.log(`端口:${PORT},服务启动成功...`);
})
评论区