The preface

Yesterday, I wrote a blog on my personal dynamic page, which involves uploading pictures.

Previously I used other people’s plug-ins and the Upload component of elementUI. But now it doesn’t work.

The page looks a little different, and changing the style of elementUI can be tiring.

It’s a very sad time. (Lazy!)

This is the effect of page development, much like the feeling of QQ space.


A, selection

1. Option 1: Rewrite the series yourself.

I referred to XHR and Fetch

Document Address:

XMLHttpRequest:https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest

fetch:https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API

XHR is actually the most used HTTP core function, used in jquery.Ajax in the early days and axios today.

Fetch, less compatible, but the latest standard function. There’s not so much clutter anymore. Make XHR look like Ajax.

Conclusion:

In the case of XHR, you need to write the entire Ajax function yourself

Fetch is a new standard, the disadvantage is that ie does not support all systems, and edge does not support some.

Ouch: Just lazy. I don’t want to write it myself. (Also: There are a lot of things to consider when writing your own package)

2, choose axios

Well, there’s not much to say. I’m not going to pick JQ.

And I wrapped Axios myself early on to create ajax functions for my own projects. So this encapsulation is to reuse the ajax functions that you’ve wrapped.

I’m going to show you what’s going on inside axios as a whole.

The main character is the axios.upload in the bottom half

"use strict";
import Vue from "vue";
import axios from "axios";
import VueAxios from "vue-axios";

import { Notification, Loading } from "element-ui";
import apiList from "./api-list"; // const store = require("store");
import control from "@/common/control_center/index"; Vue.use(VueAxios, axios); Vue.prototype.$api= apiList; // Bind interface list data to vUE global // customize message prompt function informationletCustomMsg = {// Success message sucIfno(info) {Notification({title:"Bingo!.type: "success", message: info }); }, // Error message errIfno(info) {Notification({title:"Wrong answer?".type: "error", message: info }); }}; // Authorize functions wrap const authorize = herders => {const user_info = store.get("user_info");
  if (user_info && user_info.sign) {
    herders.Authorization = user_info.sign;
    return herders;
  } else {
    returnherders; }}; // The axios function wraps const ajax = ({url ="",
  loading = falseBaseURL = apilist. baseURL, data = {}, headers = {"Content-Type": "application/json; charset=UTF-8"}, // header information processing method ="get",
  success = false// Success message error =trueTimeout = 1000}) => {const filter = record => {for (let key inrecord) { ! record[key] && delete record[key]; }returnrecord; }; // Interface global loading promptlet loadingInstance = "";
  if(loading ! = =false) {
    loadingInstance = Loading.service({
      lock: true, text: loading ! = =true ? loading : "Trying to load...",
      spinner: "el-icon-loading",
      background: "Rgba (0, 0, 0, 0.5)"
    });
  }
  returnNew Promise((suc, err) => {method = method.tolocalelowerCase (); // Set headers = authorize(headers); axios({ url: url, baseURL: baseURL, headers: headers, method: method, [method ==="post" ? "data" : "params"]: filter(data), timeout: timeout }) .then(response => { loadingInstance && loadingInstance.close(); // Refresh the password and determine whether to log out of the interfaceif(! control.refresh_sign_or_out(response)) { customMsg.errIfno("Data exception, log out"); err(response); } const res = response.data; /*data = {code: 0, // 0 successful, 1 failed MSG:""// Error message data:""/ / data}; * /if(res && res.code === 0) { success ! = =false &&
            customMsg.sucIfno(success === true ? "Information processing successful" : success);

          suc(res);
        } else{ error ! = =false &&
            customMsg.errIfno(res.msg ? res.msg : "Message processing failure"); err(res); } }) .catch(e => { console.log(e); loadingInstance && loadingInstance.close(); error ! = =false ? customMsg.errIfno("Abnormal interface") : false; // Catch indicates that the abnormal part of the network has nothing to do with the result returned by the back-end. Err (e); }); }); }; // Exposed Ajax functions to further encapsulate throttling and anti-shakinglet shakeTime = ""; Axios.ajax = options => {// parameter preprocessinglet shake = options.shake || false; / / is not equal tofalseDirect transfertrueOr anti-shake time // anti-shake function processingif (shake === false) {// No shaking treatmentreturn new Promise((suc, err) => {
      ajax(options)
        .then(e => {
          suc(e);
        })
        .catch(e => {
          err(e);
        });
    });
  } else{// Apply anti-shake treatmentreturn new Promise((suc, err) => {
      shakeTime && clearTimeout(shakeTime);
      letcallNow = ! shakeTime;if (callNow) {
        ajax(options)
          .then(e => {
            suc(e);
          })
          .catch(e => {
            err(e);
          });
      }
      shakeTime = setTimeout( () => { shakeTime = null; }, // see the annotation shake ===true? 700 : shake ); }); }}; Axios. upload = options => {// Concatenate data to uri address const new_url = obj => {if (obj) {
      let fields = "";
      for (let key in obj) {
        fields = fields + `${key}=${obj[key]}`;
      }
      return "?" + fields;
    } else {
      return ""; }}; options.fdata = options.fdata ||""; / / file upload the url of the splicing address options. The success = options. Success | |"File uploaded successfully";
  options.url = options.url + new_url(options.fdata);

  options.headers = options.headers || {};
  let header = { "Content-Type": "multipart/form-data" };
  for (let i in header) {
    options.headers[i] = header[i];
  }

  options.method = "post";
  options.multiple = options.multiple || false; // Multiple files, defaultfalse// File type validation, notice the array passed in, default ["image/jpeg"."image/png"]
  options.type = options.type || ["image/jpeg"."image/png"]; options.size = options.size || 0; / / file size limit, the default 0 options. The Max = options. The Max | | 5. // Upload a maximum of several files // File verification processinglet input = document.createElement("input");
  input.type = "file";
  options.multiple ? (input.multiple = "multiple") : "";
  input.click();

  return new Promise((suc, err) => {
    let type = options.type;
    input.addEventListener("input", watchUpload, false);
    functionWatchUpload (event) {// Remove the listenerlet remove = () => {
        input.removeEventListener("input", watchUpload, false); input = null; }; const file = event.path[0].files; const len = file.length; // Limit the number of filesif (len > options.max) {
        err(file);
        remove();
        customMsg.errIfno("Number of files exceeds" + options.max);
        return false;
      }
      let formData = new FormData();
      for (leti = 0; i < len; I++) {// file size limitif(options.size ! == 0 && file[i].size / 1024 / 1024 > options.size) { err(file[i]); remove(); customMsg.errIfno(file[i].name +"File exceeds specified size");
          return false; } // File type restrictionif(type.length > 0 && ! type.includes(file[i].type)) { err(file); remove(); customMsg.errIfno(file[i].name +"File type is" + file[i].type);
          return false;
        }
        formData.append("dhtUpload", file[i], file[i].name); } options.data = formData; // Finally upload the file options.baseURL =""; ajax(options) .then(e => { suc(e); }) .catch(e => { err(e); }); }}); };export default axios;Copy the code

Start writing the upload function

Notice that it doesn’t make any difference if you use Axios directly. Don’t be fooled by my own encapsulation of Ajax.

1. Do not display input elements on the page

The original intention here is that I do not have an input element on the page but as a function. I see a lot of tutorials on the web that predefine the input node

Here I put myself in an input-based color picker. The file is similar to this

// Color pickercolorSelect() {
  let input = document.createElement("input");
  input.type = "color";
  input.click();
  input.addEventListener("input", watchColorPicker, false);
  functionwatchColorPicker(event) { //console.log(event.target.value); / / remove the listening input. RemoveEventListener ("input", watchColorPicker, false);
    input = ""; }}Copy the code

What is the value of the input listener



We want the value in path. Look inside the path



Notice that there is only an input element here, and we take the array of files in the input to be our file.

Let’s take a look at a single file



Here we can see the size and type of each file. We’ll use that later. For example, we limit the number of file uploads, file size and file type

3, understandFormData()

I’m just going to put the MDN document here.

https://developer.mozilla.org/zh-CN/docs/Web/API/FormData/Using_FormData_Objects

In fact, all we need to know is that it works like this.

let formData = new FormData();
formData.append("dhtUpload", file[i], file[i].name);Copy the code

Append parameter: file name, file, file name

I don’t really get it here

Take a look at a situation in the elementUI source code.

formData.append(option.filename, option.file, option.file.name);Copy the code

I don’t know much about it.

4. Learn about Ajax functions

const ajax = ({
  url = "",
  loading = falseBaseURL = apilist. baseURL, data = {}, headers = {"Content-Type": "application/json; charset=UTF-8"}, // header information processing method ="get",
  success = false// Success message error =trueTimeout = 1000})Copy the code

These are the arguments to my Ajax function.

5. Preprocess parameters of upload function

Const new_URL = obj => {const new_url = obj => {if (obj) {
    let fields = "";
    for (let key in obj) {
      fields = fields + `${key}=${obj[key]}`;
    }
    return "?" + fields;
  } else {
    return ""; }}; options.baseURL =""; / / individual processing, need to be compatible with previous elementui plug-in, uploading, and options. Fdata = options. Fdata | |""; / / file upload the url of the splicing address options. The success = options. Success | |"File uploaded successfully";
options.url = options.url + new_url(options.fdata);
options.loading = options.loading || true;

options.headers = options.headers || {};
options.headers["Content-Type"] = "multipart/form-data";

options.method = "post";
options.multiple = options.multiple || false; // Multiple files, defaultfalse// File type validation, notice the array passed in, default ["image/jpeg"."image/png"]
options.type = options.type || ["image/jpeg"."image/png"]; options.size = options.size || 0; / / file size limit, the default 0 options. The Max = options. The Max | | 5. // Upload a maximum of several filesCopy the code

Looks like a lot of parameters. Not much.

Analyze:

Fdata: Because the file upload address must be diverse. But we also need to pass some information outside of the file. You can actually concatenate parameters in the address bar. The back end is also available.

New_url and fData exist for this purpose.

Success: indicates a simple prompt for file uploading. This is because the Ajax wrapper wrapped the message inside, but it succeeded without the message by default.

Headers: Not much to say about this, but to ensure that headers might exist elsewhere, it is set this way. Content-type: “multipart/form-data” this is required

Loading: A loading prompt is displayed in full screen during file loading

Method: The value must be Post

Multiple: The missing point here is to control whether a file is multiple or single file uploaded

Type: file type control. We pass in an array. Default controls image files. What defaults are passed to restrict these types

Size: limits the size of each file. 0 does not limit the size

Max: Limit the number of files. Single files are meaningless

6. Create the Input object and preprocess the parameters.

// File validation processinglet input = document.createElement("input");
input.type = "file";
options.multiple ? (input.multiple = "multiple") : "";
input.click();Copy the code

Multiple = multiple; multiple = multiple

7, create input listener, get file object information, pay attention to the outer layer with promise encapsulation

I’m going to put the whole control part here and break it down

return new Promise((suc, err) => {
  let type = options.type;
  input.addEventListener("input", watchUpload, false);
  functionwatchUpload(event) { //console.log(event); // Remove the listenerlet remove = () => {
      input.removeEventListener("input", watchUpload, false); input = null; }; const file = event.path[0].files; const len = file.length; // Limit the number of filesif (len > options.max) {
      remove();
      customMsg.errIfno("Number of files exceeds" + options.max);

      err(file);
      return false;
    }
    let formData = new FormData();
    for (leti = 0; i < len; I++) {// file size limitif(options.size ! == 0 && file[i].size / 1024 / 1024 > options.size) { remove(); customMsg.errIfno(file[i].name +"File exceeds specified size");

        err(file[i]);
        return false; } // File type restrictionif(type.length > 0 && ! type.includes(file[i].type)) { remove(); customMsg.errIfno(file[i].name +"File type is" + file[i].type);

        err(file);
        return false;
      }
      formData.append("dhtUpload", file[i], file[i].name); } options.data = formData; Then (e => {suc(e); }) .catch(e => { err(e); }); // Clear the listener if there is no problem. remove(); }});Copy the code

8, get the file array information, note that you do not select the file will not trigger the listener

//console.log(event); // Remove the listenerlet remove = () => {
      input.removeEventListener("input", watchUpload, false); input = null; }; const file = event.path[0].files; const len = file.length; // Limit the number of filesif (len > options.max) {
      remove();
      customMsg.errIfno("Number of files exceeds" + options.max);
      err(file);
      return false;
    }
    let formData = new FormData();Copy the code

Look at the notes here and you know what I did.

First: I get an array of files. And take care of the removal listeners in advance, as they will be used in many places.

Second: I judge the file limit. If so, the promise (err(file) in the code) returns an error; And unlog the listener

Third: Declare the formData object first

9. Restrict each element and add to formData

for (leti = 0; i < len; I++) {// file size limitif(options.size ! == 0 && file[i].size / 1024 / 1024 > options.size) { remove(); customMsg.errIfno(file[i].name +"File exceeds specified size");

    err(file[i]);
    return false; } // File type restrictionif(type.length > 0 && ! type.includes(file[i].type)) { remove(); customMsg.errIfno(file[i].name +"File type is" + file[i].type);

    err(file);
    return false;
  }
  formData.append("dhtUpload", file[i], file[i].name);
}
options.data = formData;Copy the code

It should be pretty obvious here. I walk through each file and impose limits on file size and file type.

Finally, the passed formData data is assigned to the Ajax data object

10, all is well, call the original package ajax

Then (e => {suc(e); }) .catch(e => { err(e); }); // Clear the listener if there is no problem. remove();Copy the code

Three, complete use.

this.axios
  .upload({
    url: this.$api.static().upload_pictures
  })
  .then(e => {
    console.log("Success", e);
  })
  .catch(e => {
    console.log("Error", e);
  });Copy the code