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-fileGets 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