First introduce the overall process
Basic use of IndexDB
- Create (open) the database
let that = this;
const dbName = "databaseName"; // Database name
const tablename = "tableName"; / / the name of the table
const dbVersion = 1.0; // Database version
// Instantiate the IndexDB data context based on the browser type
let indexedDB =
window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;
if ("webkitIndexedDB" in window) {
window.IDBTransaction = window.webkitIDBTransaction;
window.IDBKeyRange = window.webkitIDBKeyRange;
}
H5AppDB.indexedDB = {};
H5AppDB.indexedDB.db = null;
// Error information is displayed
H5AppDB.indexedDB.onerror = function(e) {
console.log(Error message:, e);
};
H5AppDB.indexedDB.open = function(version) {
/ / the initial IndexDB
var request = indexedDB.open(dbName, version || dbVersion);
request.onsuccess = function(e) {
console.log("Database opened successfully:" + dbName);
H5AppDB.indexedDB.db = e.target.result;
// Open the database
};
// If the versions are inconsistent, upgrade the versions
request.onupgradeneeded = function(e) {
console.log("Start upgrading database.");
H5AppDB.indexedDB.db = e.target.result;
var db = H5AppDB.indexedDB.db;
if (db.objectStoreNames.contains(tableName)) {
db.deleteObjectStore(tableName);
}
let store = db.createObjectStore(tableName, {
keyPath: "md5"
}); // Primary key required in NoSQL type database, unique
store.createIndex("fileId"."fileId", { unique: false }); // create a lookup index
store.createIndex("fileId"["fileId"."index"] and {unique: false }); // Multi-conditional index
};
request.onfailure = H5AppDB.indexedDB.onerror;
};
Copy the code
- Insert/update data – The key to insert data is updated if the table already exists
H5AppDB.indexedDB.addTodo = function(data, name) {
var db = H5AppDB.indexedDB.db;
var trans = db.transaction([name || tablename], "readwrite");
var store = trans.objectStore(name || tablename);
// Data is stored as objects, reflecting the flexibility of noSQL-type databases
var request = store.put(data); // Save data
request.onsuccess = function() {
console.log("Added successfully");
};
request.onerror = function(e) {
console.log("Error adding:", e);
};
};
Copy the code
3. Delete data
H5AppDB.indexedDB.deleteTodo = function(id, name) {
var db = H5AppDB.indexedDB.db;
var trans = db.transaction([name || tablename], "readwrite");
var store = trans.objectStore(name || tablename);
var request = store.delete(id); // Delete by primary key
request.onsuccess = function() {
console.log("Deleted successfully");
};
request.onerror = function(e) {
console.log("Error deleting:", e);
};
};
H5AppDB.indexedDB.open(1.0);
},
Copy the code
4. Find the data
H5AppDB.indexedDB.getAllTodoItems = function(name, key, callback, ... value) {
//let todos = "";
var db = H5AppDB.indexedDB.db;
var trans = db.transaction([name || tablename], "readwrite"); // Open objects with things
var store = trans.objectStore(name || tablename); // Get the value of the object
var index = store.index(key);
// Get everything in the store;
var keyRange = IDBKeyRange.only(value.length > 1 ? value : value[0]);// If it is a single condition query, you need to pass in a specific data value. If it is a multi-condition query, you need to pass in an array of data values
var cursorRequest = index.openCursor(keyRange); // open the table with index 0
let res = [];
cursorRequest.onsuccess = function(e) {
let result = e.target.result;
if(!!!!! result ===false) return;
res.push(result.value);
result.continue(); // polling reads are performed here
};
cursorRequest.onerror = H5AppDB.indexedDB.onerror;
trans.oncomplete = () = > {// Execute after the transaction is executed
callback(res);// Since indexDB is executed asynchronously, a callback function can be passed to ensure that the data is queried before further operations are performed on the data
};
};
Copy the code
Breakpoint continuingly
File fragmentation
Dynamic sharding is not adopted here. Dynamic sharding can determine the size of the next sharding according to the time it takes to upload the previous sharding.
// The file is sharded and pushed into the fileChunkedList array
const chunkSize=2 * 1024 * 1024;// Fragment size
for (let i = 0; i < optionFile.size; i = i + chunkSize) {
const tmp = optionFile.slice(
i,
Math.min(i + chunkSize, optionFile.size)
);
fileChunkedList.push(tmp);
}
Copy the code
Process file partitioning information
fileChunkedList = fileChunkedList.map((item, index) = > {
const formData = new FormData();
const ans = {};
if (option.data) {
Object.keys(option.data).forEach(key= > {
formData.append(key, option.data[key]);
});
}
// See what the backend needs to pass, or you can append additional parameters
formData.append(
"multipartFile",
that.isPurse ? item.formData : item
); // file,isPurse===true
formData.append("key", option.file.name); / / file name
ans.formData = formData;
ans.index = index;
return ans;
});
Copy the code
Fragment indicates the MD5 digest signature
In order not to block the upload progress, the browser intermittent requestIdleCallback method is used for MD5 encryption.
// Intermittently calculates fragment MD5
const calculateHashIdle = async chunks => {
return new Promise(resolve= > {
const spark = new SparkMD5.ArrayBuffer();
let count = 0;
const appendToSpark = async file => {
return new Promise(resolve= > {
const reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = e= > {
const md5 = spark.append(e.target.result);
resolve(md5);
};
});
};
const workLoop = async deadline => {
// There is a task, and the current frame is not finished
while (count < chunks.length && deadline.timeRemaining() > 1) {
if(! chunks[count]) {return;
}
await appendToSpark(chunks[count]);
chunks[count].md5 = spark.end();
H5AppDB.indexedDB.addTodo({// Since the key of the partitioned table is partitioned MD5, perform insert here to ensure that the MD5 of the partitioned table has been generated. chunks[count], fileId formData }); count++; resolve(chunks[count]); }window.requestIdleCallback(workLoop);
};
window.requestIdleCallback(workLoop);
});
};
Copy the code
Control concurrent upload
function sendRequest(chunks, limit = 3) {
return new Promise((resolve, reject) = > {
const len = chunks.length;
let counter = 0;
let isStop = false;// Whether to stop uploading
const start = async() = > {if (isStop) {
return;
}
const item = chunks.shift();
if(! item) {return;
}
if(! item.md5) {RequestIdleCallback is used for MD5 encryption, so it is possible that the browser is very busy and the current block has not been encrypted.
await calculateHashIdle([item]);
}
if (item)
if (item) {
let config = {
method: "POST".url: url,
data: item.formData,
onUploadProgress: e= > {
percentage[item.index] = e.loaded;
updataPercentage(e);// Process the progress bar
},
headers: {
"Content-Type": "multipart/form-data".md5: item.md5
},
withCredentials: true
};
axios(config)
.then(response= > {
if (response.data.code) {
if(response.data.code ! = ="0") {
isStop = true;
reject(response);
} else {
if (counter === len - 1) {// Indicates that the last block is uploaded successfully
resolve(response.data);
H5AppDB.indexedDB.deleteTodo(// drop the file table
fileId,
"fileCollection"
);
} else {
counter++;
H5AppDB.indexedDB.addTodo(// Update the file table to update the upload progress
{
...data
},
"fileCollection"
);
start();
}
H5AppDB.indexedDB.deleteTodo(// Delete the uploaded blocks
response.config.headers.md5
);
}
}
})
.catch(err= > {
that.$message({
message: "Network request error!".type: "error".showClose: true}); reject(err); }); }};while (limit > 0) {// Control concurrency
setTimeout(() = > {
start();
}, Math.random() * 1000);
limit -= 1; }}); }Copy the code
Process the file upload progress bar
// Update the upload progress bar percentage method
const updataPercentage = e= > {
let loaded = (that.uploaded / 100) * optionFile.size; // Total size of uploaded files
console.log(loaded);
percentage.forEach(item= > {
loaded += item;
});
e.percent = (loaded / optionFile.size) * 100;
if (that.value >= parseFloat(Number(e.percent).toFixed(0))) return;
that.value = parseFloat(Number(e.percent).toFixed(0));
};
Copy the code
The interface information is initialized after being refreshed
const getDataCallback = res= > {
if (res.length) {
this.fileOption = {
file: {
size: res[0].fileSize,// Total size of data
name: res[0].fileName
}
};
res.forEach(item= > {
that.fileList[item.fileType] = [// Display file information
{
name: item.fileName,
type: item.fileType,
fileId: item.fileId
}
];
});
this.value = res[0].uploaded;// Displays the progress bar
this.uploaded = res[0].uploaded;// Percentage of uploaded data
this.isPurse = true; }}; H5AppDB.indexedDB.getAllTodoItems("fileCollection".// Query the data table
"union".// The index of the query
getDataCallback,// The ruin function executed after the query is complete
data// Multiple query conditions can be sent
);
Copy the code