携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情
前端熟知的上传一般多为:上传图片、上传Excel、上传Word...
我们根据是否上传到服务器,是否需要前端解析分为两类
准备工作
-
使用RAM用户或STS方式访问
由于阿里云账号AccessKey拥有所有API访问权限,建议遵循阿里云安全最佳实践。如果部署在服务端可以使用RAM用户或STS来进行API访问或日常运维管控操作,如果部署在客户端请使用STS方式来进行API访问。更多信息,请参见访问控制。
-
设置跨域资源共享(CORS)
具体操作,请参见设置跨域资源共享。
通过浏览器直接访问OSS时,CORS配置规则要求如下:
- 来源:设置精准域名(例如
www.aliyun.com)或带有通配符星号(*)的域名(例如*.aliyun.com)。 - 允许Methods:请根据实际使用场景,选择不同的Methods。例如分片上传时,设置为PUT;删除文件时,设置为DELETE。
- 允许Headers:设置为
*。 - 暴露Headers:设置为
ETag、x-oss-request-id和x-oss-version-id。
- 来源:设置精准域名(例如
上传到服务器 - OSS上传
我司使用的是阿里云的OSS上传。
可以上传图片、Word、Pdf...
浏览器支持
- IE(>=10)
- Edge
- 主流版本的Chrome、Firefox、Safari
- 主流版本的Android、iOS、Windows Phone系统默认浏览器
配置OSS
1. 安装sdk插件
npm install ali-oss
2. 配置参数变量
const OSS = require('ali-oss');
const client = new OSS({
// yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
region: 'yourRegion',
// 从STS服务获取的临时访问密钥(AccessKey ID和AccessKey Secret)。
accessKeyId: 'yourAccessKeyId',
accessKeySecret: 'yourAccessKeySecret',
// 从STS服务获取的安全令牌(SecurityToken)。
stsToken: 'yourSecurityToken',
refreshSTSToken: async () => {
// 向您搭建的STS服务获取临时访问凭证。
const info = await fetch('your_sts_server');
return {
accessKeyId: info.accessKeyId,
accessKeySecret: info.accessKeySecret,
stsToken: info.stsToken
}
},
// 刷新临时访问凭证的时间间隔,单位为毫秒。
refreshSTSTokenInterval: 300000,
// 填写Bucket名称。
bucket: 'examplebucket'
});
3. Component组件
<div class="upload-container">
<el-upload
class="upload-container"
:data="dataObj"
:multiple="false"
:show-file-list="false"
drag
:on-change="getFile"
:auto-upload="false"
>
<i class="el-icon-upload" />
<div class="el-upload__text">
将文件拖到此处,或<em>点击上传</em>
</div>
</el-upload>
<div class="image-preview">
<div v-show="imageUrl.length>1" class="image-preview-wrapper">
<img :src="imageUrl+'?imageView2/1/w/200/h/200'">
<div class="image-preview-action">
<i class="el-icon-delete" @click="rmImage" />
</div>
</div>
</div>
</div>
4. 上传的关键代码
OSS支持两种使用方式
- 同步方式:基于ES7规范的async/await,使得异步方式同步化。
- 异步方式:与Callback方式类似,API接口返回Promise,使用then()处理返回结果,使用catch()处理错误。
这里,使用的时第一种同步方式
无论同步方式还是异步方式,均使用new OSS()创建client。
getFile (file, fileList) {
// 校检文件类型
if (this.fileType.length > 0) {
let filename = file.name.split('.')[1]
const isTypeOk = this.fileType.some((type) => {
return type.indexOf(filename) > -1
})
if (!isTypeOk) {
this.$message.error(
this.$store.getters.lableProp[2342] + this.fileType.join(',')
)
return false
}
}
// 校检文件大小
if (this.fileSize > 0) {
const isLt = file.size / 1024 / 1024 < this.fileSize
if (!isLt) {
this.$message.error(
this.$store.getters.lableProp[2343] + this.fileSize + 'MB'
)
return false
}
}
this.getBase64(file.raw).then((source) => {
this.source = source
let data = this.base64ToBlob(this.source)
this.putObject(file, data)
})
},
// 转换成base64
getBase64 (file) {
return new Promise(function (resolve, reject) {
const reader = new FileReader()
let imgResult = ''
reader.readAsDataURL(file)
reader.onload = function () {
imgResult = reader.result
}
reader.onerror = function (error) {
reject(error)
}
reader.onloadend = function () {
resolve(imgResult)
}
})
},
async putObject (file, data) {
try {
const result = await this.ossClient.put(
parseTime(new Date().getTime(), '{y}{m}{d}') +
'/' +
file.uid +
'.' +
file.name.split('.')[1],
data
)
this.dialogVisible = false
this.$emit('onSuccessImg', result.name)
this.$message({
message: '上传成功'
type: 'success'
})
} catch (error) {
console.log('Upload OSS Error')
console.log(error)
this.$message.error('上传失败')
}
}
删除图片
this.ossClient.delete(文件名)
前端解析 - Excel上传
前端解析,就是将文件解析成数组,然后生成一个results和header。
html
<div>
<input
ref="excel-upload-input"
class="excel-upload-input"
type="file"
accept=".xlsx, .xls, .csv"
@change="handleClick"
/>
<div
class="drop center"
@drop="handleDrop"
@dragover="handleDragover"
@dragenter="handleDragover"
>
<span class="note">{{ lableProp[1826] }}</span>
<el-button
:loading="loading"
style="margin-left: 16px"
size="mini"
type="primary"
@click="handleUpload"
>
{{ lableProp[1827] }}
</el-button>
</div>
<div>
{{ lableProp[2840] }}
</div>
</div>
css
.excel-upload-input {
display: none;
z-index: -9999;
}
.drop {
border: 2px dashed #bbb;
width: 100%;
height: 160px;
margin: 0 auto;
border-radius: 5px;
text-align: center;
/* color: #bbb; */
position: relative;
}
.note {
font-size: 14px;
}
js
import XLSX from 'xlsx'
export default {
props: {
beforeUpload: Function, // eslint-disable-line
onSuccess: Function// eslint-disable-line
},
data() {
return {
loading: false,
excelData: {
header: null,
results: null
}
}
},
methods: {
generateData({ header, results }) {
this.excelData.header = header
this.excelData.results = results
this.onSuccess && this.onSuccess(this.excelData)
},
handleDrop(e) {
e.stopPropagation()
e.preventDefault()
if (this.loading) return
const files = e.dataTransfer.files
if (files.length !== 1) {
this.$message.error('Only support uploading one file!')
return
}
const rawFile = files[0] // only use files[0]
if (!this.isExcel(rawFile)) {
this.$message.error('Only supports upload .xlsx, .xls, .csv suffix files')
return false
}
this.upload(rawFile)
e.stopPropagation()
e.preventDefault()
},
handleDragover(e) {
e.stopPropagation()
e.preventDefault()
e.dataTransfer.dropEffect = 'copy'
},
handleUpload() {
this.$refs['excel-upload-input'].click()
},
handleClick(e) {
const files = e.target.files
const rawFile = files[0] // only use files[0]
if (!rawFile) return
this.upload(rawFile)
},
upload(rawFile) {
this.$refs['excel-upload-input'].value = null // fix can't select the same excel
if (!this.beforeUpload) {
this.readerData(rawFile)
return
}
const before = this.beforeUpload(rawFile)
if (before) {
this.readerData(rawFile)
}
},
readerData(rawFile) {
this.loading = true
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = e => {
const data = e.target.result
const workbook = XLSX.read(data, { type: 'array' })
const firstSheetName = workbook.SheetNames[0]
const worksheet = workbook.Sheets[firstSheetName]
const header = this.getHeaderRow(worksheet)
const results = XLSX.utils.sheet_to_json(worksheet)
this.generateData({ header, results })
this.loading = false
resolve()
}
reader.readAsArrayBuffer(rawFile)
})
},
getHeaderRow(sheet) {
const headers = []
const range = XLSX.utils.decode_range(sheet['!ref'])
let C
const R = range.s.r
/* start in the first row */
for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */
const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })]
/* find the cell in the first row */
let hdr = 'UNKNOWN ' + C // <-- replace with your desired default
if (cell && cell.t) hdr = XLSX.utils.format_cell(cell)
headers.push(hdr)
}
return headers
},
isExcel(file) {
return /\.(xlsx|xls|csv)$/.test(file.name)
}
}
}
组件调用
<upload-excel-component :on-success="handleSuccess" :before-upload="beforeUpload" />
beforeUpload(file) {
const isLt1M = file.size / 1024 / 1024 < 1
if (isLt1M) {
return true
}
this.$message({
message: 'Please do not upload files larger than 1m in size.',
type: 'warning'
})
return false
},
handleSuccess({ results, header }) {
this.tableData = results
this.tableHeader = header
}