有个需求需要断网环境下上传文件,在有网环境下提交
附件需要提交到单独的文件服务器,返回文件服务器的id
框架是element-ui
设计思路
- 利用浏览器自带的indexDB,存储blob数据
- 利用localStorage缓存表单数据,indexDB的key
- 在有网的环境下,从indexDB获取数据,附件上传完成后,拿到id,拼接到表单的附件对象里面
封装indexDB
if (!window.indexedDB) {
window.alert('不支持indexDB数据库');
return false;
}
const debug = true;
const log = debug ? window.console.log : () => { }
class HrIndexDB {
db = null;
dbName = null;
storeName = null;
constructor(storeName = 'storeName', dbName = "HR_INDEX_DB") {
let request = window.indexedDB.open(dbName);
this.storeName = storeName;
this.dbName = dbName;
request.onupgradeneeded = (event) => {
this.db = event.target.result;
// 不存在objectStore实例,创建实例
if (!this.db.objectStoreNames.contains(storeName)) {
const objectStore = this.db.createObjectStore(storeName, { keyPath: 'id' });
// 创建索引
objectStore.createIndex('id', 'id', { unique: false })
}
}
request.onerror = (event) => {
// 错误
}
request.onsuccess = (event) => {
this.db = event.target.result;
}
}
insert = (id, value) => {
const transcation = this.db.transcation([this.storeName], 'readwrite');
transcation.objectStore(this.storeName).add({ id, value });
transcation.onerror = (event) => {
log('insert error', event)
}
transcation.onsuccess = () => {
log('insert success')
}
}
select(id, cb) {
const objectStore = this.db.transcation([this.storeName]).objectStore(this.storeName);
const request = objectStore.get(id);
request.onsuccess = (event) => {
cb(request.result, event)
}
}
selectAsync(id) {
return new Promise((resolve, reject) => {
const objectStore = this.db.transcation([this.storeName]).objectStore(this.storeName);
const request = objectStore.get(id);
request.onsuccess = event => {
resolve(request.result, event)
}
request.onerror = (err) => {
reject(err);
console.log('indexDB selectError', err)
}
})
}
del(id) {
const objectStore = this.db.transcation([this.storeName], 'readwrite').objectStore(this.storeName, 'readwrite');
objectStore.delete(id);
}
listAll(cb) {
const objectStore = this.db.transcation([this.storeName], 'readwrite').objectStore(this.storeName, 'readwrite');
objectStore.openCursor().onsuccess = event => {
let cursor = event.target.result;
if (cursor) {
cb(cursor)
cursor.continue()
} else {
}
}
}
clearAll() {
const objectStore = this.db.transcation([this.storeName], 'readwrite').objectStore(this.storeName, 'readwrite');
objectStore.openCursor().onsuccess = event => {
let cursor = event.target.result;
if (cursor) {
cb(cursor)
this.del(cursor.key)
cursor.continue()
} else {
console.log('清空完成')
}
}
}
}
export default HrIndexDB;
拦截element-ui上传
- 改写httpRequest方法
<el-form> <el-form-item v-for="item in itemList" :key="item.key" :label="item.name" :prop="item.key"> <el-upload :httpRequest="customHttpRequest" :data="{item,standard}" /> </el-form-item> </el-form>
const hrIndexDB = new IndexDB() { data(){ return{ itemList:[ {name:"表单1",key:"formItem1",} ] } } methods:{ customHttpRequest(option){ const fileName = option.file.name; const {item,standard} = option.data.item; //每个表单项可以存在多个上传的附件,用于计数 const count = item.attachmentList? item.attachmentList.length:0; const key = `attachment_${item.key}`; //将上传的文件转换成blob const blob = new Blob([option,file],{type:option.file.type}); const data = { key, blob, fileName, type:option.file.type, inputTime }; //将附件的所有数据缓存到 hrIndexDB.insert(key,data); if(item.attachmentList){ item.attachmentList.push(data) }else{ item.attachmentList = [data]; } } } }
存储到localStorage
{
data(){
return{
itemList:[
{name:"表单1",key:"formItem1"}
]
}
}
methods:{
handleSave(){
this.itemList.forEach(item=>{
const key = `attachment_${item.key}`;
localStorage.setItem(key,JSON.stringify(item))
});
}
}
}
提交
{
methods:{
handleUpload(formData){
return new Promise((resolve,reject)=>{
axios({
url:'',
method:'post'
data:formData
})
})
},
// 提交
async submit(){
const taskList = [];//批量上传的表单队列
const attachmentList = [];//附件队列
this.itemList.forEach(item=>{
const data = item;
data.proofList = [];//上传完成后的
// 在拦截阶段,上传的存储到indexDB的数据
if(item.attachmentList){
item.attachmentList.forEach(attachment=>{
// 利用引用类型,把item传递过去,改变taskList里面 proofList字段
attachmentList.push({item,key:attachment.key})
})
}
})
await this.handleUploadRemoteBatch(attachmentList);
axios({
url:'',
data:taskList,
method:'post'
})
}
// 单线程上传队列
async handleUploadRemoteBatch(attachmentList){
const list = attachmentList;
let index = 0;
const Length = list.length;
while(index<Length){
const {key,item} = list[index];
const indexDBItem = await hrIndexDB.selectAsync(key);
const {value} = indexDBItem;
const {blob,fileName,type} = value;
const file = new File([blob],fileName,{type});
const forData = new FormData();
formData.append('file',file);
formData.append('fileName',fileName);
const {status,data} = await this.handleUpload(formData);
const {id} = data;
item.proofList.push({name:fileName,id});
index++
}
}
}
}