Recently, a requirement was made to let the user upload a video file with a maximum of 300M locally. The following is the record of the front-end part.
Demand analysis point
- Check the type of files uploaded by the user, the size of the memory, and the playing time of the video
- If the video is too large, it needs to be fragmented and uploaded. Record the number of pieces of data this time, and re-upload can be realized to continue uploading the last clip instead of re-uploading
- The same video needs to be marked and cannot be uploaded repeatedly to reduce the memory pressure of the server
Interaction style
Next, how to let the user upload the file first? Input tag type=file can let the user upload the file, here I am in Vue project.
<input ref="videoInput" type="file" title="Select video File" @change="handleUpload" />
Copy the code
At this point, users can already upload files locally, but there is a problem here, when the user has already uploaded a video file, if the user uploads again, the prompt message will be displayed first, and the original video will be overwritten if the user uploads again. However, when the user clicks the input button, it already triggers the popover of the selection file, which can’t be blocked. Besides, the native input style is ugly and comes with the name of the selected file, which is not what I want. It doesn’t fit our UI style, so my idea is to hide the input and write a new button instead. Add a click event to the button, handle extra logic in that click event, and then actively trigger the input click event with js code.
this.$messageBox.confirm("Re-uploading the video will replace the original video, do you want to continue?").then((a)= > {
this.$refs.videoInput.click();
});
Copy the code
Mark video
Next up is how to mark videos so you don’t upload the same video twice. The solution is to get the hash MD5 value of the video file. Because the MD5 of each file is unique, we only need to obtain the MD5 of the file to be uploaded in the front end and upload it to the server when uploading the file. Compared with the MD5 of the previous file, if there is the same MD5, we just need to upload the file name to the server and associate it with the previous file. There is no need to upload the same file again, which consumes storage resources, upload time, and network bandwidth.
let file = this.$refs.videoInput.files[0];
console.log(file);
Copy the code
Notice the file data that we got here, and we’ll come back to it later you can get the file file, and it can get the type of the file itself, and the memory size of the file, in bytes. There is also a slice method for cutting and slicing to obtain sub-fragment data files. However, using it directly to obtain the MD5 value is found to be inconsistent with the backend (Java). What’s next?
The first is the Spark-MD5 dependency package
SparkMD5 is a fast implementation of the MD5 algorithm. This script is based on the JKM MD5 library, which is best for browsers.
How it works :(simplified core code)
import SparkMD5 from 'spark-md5';
const fileReader = new FileReader();
fileReader.onload = function (e) {
let spark = new SparkMD5.ArrayBuffer();
spark.append(e.target.result); // Append array buffer console.info("computed hash", spark.end()); // Compute hash }; fileReader.readAsArrayBuffer(file); Copy the code
For FileReader, go to 🚀 FileReader
usebrowser-md5-file
Gets the md5 value of the file, which is further encapsulation
import BMF from "browser-md5-file";
const bmf = new BMF();
bmf.md5(
file,
(err, md5) => {
if (err) { console.log(err); } console.log(md5); / / the md5 value }, (process) => { // Calculate progress } ); Copy the code
At this time, the MD5 value of the file is consistent with that of the back end 😍!
Gets the playing duration of the video
There are two scenarios to get the duration of the video
- File data
- By playing the address
function getVideoDuration(file) {
return new Promise((resolve, reject) = > {
var audioElement;
var url;
if (typeof file == 'string') {
audioElement = new Audio(); audioElement.src = file } else { url = window.URL.createObjectURL(file); audioElement = new Audio(url); } audioElement.addEventListener("loadedmetadata".function() { var duration = audioElement.duration; // console.log(" video duration ", duration); window.URL.revokeObjectURL(url); resolve(duration) }); }) } Copy the code
Here it is necessary to say what is the data File directly obtained through the File uploaded by the user?
File is the binary object class used in the front end for File operations, and its parent is Blob. They’re specific to a file, or it’s just a file object.
For bloBs, go to 🚀 bloBs you don’t know
Upload a file
The data format of the uploaded file {” content-Type “: “multipart/form-data”}, html5 provides an API
The FormData interface provides a way to construct key/value pairs that represent FormData and can easily send the data through the xmlhttprequest.send () method, both of which are fairly straightforward. If the sent encoding type is set to “multipart/form-data”, it will use the same format as the form.
let formData = new FormData();
formData.append("file", item);
axios({
url: "xxx". method: "post". headers: { "content-type": "multipart/form-data" }, data: formData, }); Copy the code
The following is the project about the upload code logic, to give you a reference!
/ * * * id
* fileList Specifies the list after a file is fragmented* hashList Indicates the LIST of MD5 values after the file is fragmented* startIndex continues uploading the default first segment at the start of the previous upload* processFn Upload progress* / function async startMediaPartUpload(id, fileList, hashList, startIndex = 1, processFn) { let point = Math.floor(((startIndex - 1) / fileList.length) * 100); processFn && processFn(point) / / upload for (let i = startIndex - 1; i < fileList.length; i++) { const item = fileList[i]; let formData = new FormData(); formData.append("id", id); formData.append("option"."slice"); formData.append("hash", hashList[i]); formData.append("part", i + 1); formData.append("file", item); let { code, memo, content } = await mediaPartUpload(formData); let point = Math.floor(((i + 1) / fileList.length) * 100); processFn && processFn(point) if (code == "000000") { // this.$message.success(' upload OK ${content.part} '); } else { this.$message.error(memo); return Promise.reject("Upload failed."); } } } Copy the code
This article is formatted using MDNICE