Vue Image Upload Component Introduction

Vue Image Upload Component Introduction

Many projects need to use the image upload function, and its multiple use requirements. In order to avoid rebuilding the wheel, I decided to spend some time to understand it in depth, and finally encapsulated a vue image upload component.

Many projects need to use the image upload function, and its multiple use requirements. In order to avoid rebuilding the wheel, I decided to spend some time to understand it in depth, and finally encapsulated a vue image upload component.

Layout
<div class = "upload-wraper"> 
      <input type = "file" id = "upload_ele" multiple = "false" 
      accept = "image / *" @ change = "uploadFile ()" /> 
  </ div>

type = file set type to select file

whether to allow multiple selection of files

accept = "image / *" restricts the file type to image type, * including all format images

change event When the type is set to file, the starting event is change, which can also be implemented by submit

The layout here is simpler because it is a Vue component. It does not require multiple inputs to form the form, and then submits through submit. One input is uploaded through the change event.

Js

Basic information for uploading files

 let oFIle = document.getElementById ('upload-ele'). files [0];

files is a js built-in object after input is set to file. The files object has a read-only property and cannot be modified!

After printing out oFile, you can see the basic information of the file object. As follows:

isClosed: false whether it has ended, can be understood as whether the label is closed
lastModified: 1539602132000 time of last change
lastModifiedDate: Mon Oct 15 2018 19:15:32 GMT + 0800 (CST) () Last modified time
name: "D9791645A5DF19D17FD7392A080E7A28.jpg" The name of the picture
path: "/ Users / mac / Documents / D9791645A5DF19D17FD7392A080E7A28.jpg" The path of the picture is a local path
Size: 38938 picture size information unit is kb
type: 'image / jpeg' image type
webkitRelativePath: "" file related path

File Size Processing

Size judgment

(oFile.size / 1024)> 1024

Smaller

let form = new FormData (); 
  form.append ('file', oFile); 
  let xhr = new XMLHttpRequest (); 
  xhr.open ('post', url, true); 
  xhr.timeout = 30 * 1000; 
  xhr. upload.onprogress = this.progress; 
  xhr.onload = this.uploadComplete; 
  xhr.onerror = this.uploadFailed; 
  xhr.upload.onloadstart = () => { 
      let date = new Date (). getTime (); 
      let initSize = 0; 
  } 
  xhr.send (form);

XMLHttpRequest () is a JS built-in object. You can use this property to implement the processing of the request header.
xhr.open (); Request method: post, url: The address accepted by the server, true / false is asynchronous
xhr.timeout; Set the timeout period, as the case may be
xhr.ontimeout; timeout processing, generally cancel the request
xhr.upload.onprogress; process processing, progress processing of uploaded files
xhr.onload; request processed successfully
xhr.onerror; Request failed
Xhr.upload.onloadstart; Request to start processing, generally create a timestamp, initialize the size.
xhr.send (); After the request is configured, send the request

Files smaller than 1M here can be directly placed in formData, and the image object is uploaded to oss by configuring xhr. After the request is successfully obtained, the network path of the image is provided and provided to the backend.

Larger

Why: Why do we need to process pictures larger than 1M? Because files larger than 1M generally fail to upload. When uploading common ftp files, the default is 2M. When it is larger, the upload fails, so the image upload here is processed with compression greater than 1M.

how: The process is very similar to when it is less than 1M, but the image compression processing is added here.

more: Generally, when processing large files, the backend can also process, and provides an interface for larger images separately. At this time, we don't need to compress, only need to change the interface to an interface for processing larger files when it is judged to be larger than 1024KB.

How to compress?

  • Read the file object into base64 format through the FileReader object.
  • The image in base format is combined with the canvas canvas to reduce the base format image to a certain ratio and then save it back to base64 format.
  • After converting the base64 format to a Blob stream, the image can be said to be compressed.
  • The remaining steps are repeated formData, XMLHttpRequest can complete the upload of larger images. It should be noted here that when preventing files in formData, because it is a Blob stream at this time, you need to customize the file format when preventing files. The processing here is composed of Blob files + timestamps and .jpg.

Component source

<template> 
  <!-image upload component-> 
    <div> 
      <input type = "file" id = "upload-ele" multiple = "false"   
      accept = "image / *" @ change = "uploadFile (url, quality, hasApi, BigUrl) "> 
      <toast v-model =" total.isShow "type =" text "> {{total.text}} </ toast> 
    </ div> 
  </ template> 
  <script> 
  import {Indicator } from 'mint-ui'; 
  import {Toast} from 'vux'; 
  export default { 
    name: 'uploadImage', 
    components: { 
      Indicator, 
      Toast, 
    }, 
    props: { 
      'url': String,// Little and 1M api 
      'quality': Number, // Picture quality 
      'BigUrl': { 
        type: String,
        default: '', 
      }, // api for images larger than 1M 
      'hasApi': { 
        type: Boolean, 
        default: false 
      } // Whether to assign an interface to images larger than 1M 
    }, 
    data () { 
      return { 
        total: {isShow : false, text: ""} 
      } 
    }, 
    methods: { 
      uploadFile (url, quality, hasApi, BigUrl) { 
        Indicator.open (`Uploading`); 
        // files is a built-in object after input is set to file. The files object is a read-only property and cannot be modified. 
        var oFile = document.getElementById ('upload-ele'). files [0]; 
        console.log ('File Object', oFile); 
        var form = new FormData (); 
        // If the size is greater than 1M, the new compression processing
        // console.log ('File Size Unit: KB', (oFile.size / 1024)) 
        if ((oFile.size / 1024)> 1024) { 
          if (hasApi) {   
            form.append ('file', oFile) ; 
            let xhr = new XMLHttpRequest (); // XMLHttpRequest Object 
            xhr.open ('post', BigUrl, true); 
            // Method: post, url: server receive address, true / false isAsync 
            xhr.timeout = 30 * 1000; // Timeout one minute; 
            xhr.ontimeout = this.uploadTimeout; // Upload Timeout Function 
            xhr.upload.onprogress = this.progress; // Progress Function 
            xhr.onload = this.uploadComplete; // Upload Success Function 
            xhr.onerror = this.uploadFailed;// Upload Failed Funciton
            xhr.upload.onloadstart = () => { 
              let date = new Date (). getTime (); // TimeStamp Prevents Caching 
              let initSize = 0; // Init File Size Zero 
            } // Upload Start 
            xhr.send (form) ; 
          } else { 
            this.imgCompress (oFile, {quality: quality}, 
            (base64Codes) => { 
              var bl = this.convertBase64UrlToBlob (base64Codes); 
              form.append ("file", bl, "file_" + Date.parse ( new Date ()) + ".jpg"); 
              // file object 
              console.log (form); 
              let xhr = new XMLHttpRequest (); // XMLHttpRequest object 
              xhr.open ("post", url, true);
              // post method, url is the server request address, true This parameter specifies whether the request is processed asynchronously. 
              xhr.upload.onprogress = this.progress; // Progress Function 
              xhr.ontimeout = this.uploadTimeout; // Upload Timeout Function 
              xhr.onload = this.uploadComplete; // Upload Success Function 
              xhr.onerror = this.uploadFailed; // Upload Failed Funciton 
              xhr.upload.onloadstart = function () { 
                let ot = new Date (). GetTime (); // TimeStamp Prevents Caching 
                let oloaded = 0; // Init File Size Zero 
              }; // Upload Start 
              xhr.send (form); 
            }) 
          } 
        } else { 
          // small and 1M
          form.append ('file', oFile); 
          let xhr = new XMLHttpRequest (); // XMLHttpRequest Object 
          xhr.open ('post', url, true); 
          // Method: post, url: server receive address, true / false isAsync 
          xhr.timeout = 30 * 1000; // Timeout one minute; 
          xhr.ontimeout = this.uploadTimeout; // Upload Timeout Function 
          xhr.upload.onprogress = this.progress; // Progress Function 
          xhr.onload = this.uploadComplete ; // Upload Success Function 
          xhr.onerror = this.uploadFailed; // Upload Failed Funciton 
          xhr.upload.onloadstart = () => { 
            let date = new Date (). GetTime (); // TimeStamp Prevents Caching
            let initSize = 0; // Init File Size Zero 
          } // Upload Start 
          xhr.send (form); 
        } 
      }, 
      / ** 
       * @description Request Success 
       * / 
      uploadComplete (evt) { 
        let res = JSON.parse (evt. target.responseText); 
        if (evt.target.readyState == 4 && evt.target.status == 200) { 
          this. $ emit ('upload', res.result.url); 
        } else { 
          this.uploadFailed () ; 
        } 
      }, 
      / ** 
       * @description Request Failed 
       * / 
      uploadFailed (evt) { 
        Indicator.close ();
        this.total = { 
          isShow: true, 
          text: "Upload failed" 
        } 
      },   
    / **   
     * @description Timeout Function   
     * /   
    uploadTimeout (evt) {   
      this.cancleUploadFile (evt)   
      Indicator.close ();   
      this.total = {   
        isShow: true,   
        text: "Request timed out"   
      }   
    },   
    / ** e   
     * @description Upload Cancel   
     * /   
    cancleUploadFile (evt) {   
      evt.abort ();   
    },   
    / **   
     * @description Requst Loading ....   
     * /  
    progress (progressEvent) {   
      if (! progressEvent.lengthComputable) {   
        this.total = {   
          isShow: true,   
          text: "Progress read failed"   
        }   
        return false;   
      }   
      let precent = Math.floor (100 * progressEvent.loaded / progressEvent. total); 
      // Upload Progress   
      if (precent <100) {   
        Indicator.open (`$ {precent}%`);   
      } else {   
        Indicator.close ();   
        this.total = {     
          isShow: true,   
          text: " Upload successful "   
        }   
      }   
    },   
    / **   
      * @description image compression  
      * @param {Object} file compressed file   
      * @param {Number} width Compresses the width of the back end. The smaller the width, the smaller the bytes.   
      * /   
    imgCompress (file, width, callBack) {   
      var ready = new FileReader ();   
      ready .readAsDataURL (file);   
      ready.onload = () => {   
        this.canvasDataURL (ready.result, width, callBack);   
      }       
    },   
    / **   
     * Convert base64 image url data to Blob   
     * @param urlData   
     * Base64 image data expressed in URL method   
     * /   
    convertBase64UrlToBlob (urlData) {   
      var arr = urlData.split (","),   
        mime = arr [0] .match (/: (. *?); /) [1],   
        bstr = atob (arr [1]),  
        n = bstr.length,   
        u8arr = new Uint8Array (n);   
      while ( 
        n-- ) {   u8arr [n] = bstr.charCodeAt (n);   
      }   
      return new Blob ([u8arr], {type: mime});   
    } ,     
    / **   
     * @description images larger than 1M are redrawn and compressed   
     * /   
    canvasDataURL (path, obj, callback) {   
      var img = new Image ();   
      img.src = path;   
      img.onload = () => {   
        / / var that = this;   
        // default compression   
        var w = this.width,   
          h = this.height,   
          scale = w / h;   
        w = obj.width | w;  
        h = obj.height || w / scale;   
        var quality = 0.7; // default image quality is 0.7   
        // generate canvas   
        var canvas = document.createElement ("canvas");   
        var ctx = canvas.getContext ("2d") ;   
        // create attribute node   
        var anw = document.createAttribute ("width");   
        anw.nodeValue = w;   
        var anh = document.createAttribute ("height");   
        anh.nodeValue = h;   
        canvas.setAttributeNode (anw);   
        canvas .setAttributeNode (anh);   
        ctx.drawImage (img, 0, 0, w, h);   
        // image quality   
        if (obj.quality && obj.quality <= 1 && obj.quality>  0) { 
          quality = obj.quality;  
        }   
        // The smaller the quality value, the blurry the drawn image   
        var base64 = canvas.toDataURL ("image / jpeg", quality);   
        // The callback function returns the value of   
        base64 callback (base64);   
      };   
    },   
  }   
}   
</ script>   
<style scoped>   
  .upload-wraper {   
    width: 100%;   
    height: 100%;   
  }   
</ style>

This is the public account project, so the third-party plugins introduced here are mint-ui and vux. The specific situation depends on the situation.

This component is exceptionally encapsulated. The backend is enough to process larger images. If the backend has already processed it, it is called directly. If there is no processing, compression processing is performed,

Encapsulation of components, which can be modified flexibly

Introduced as follows:

<upload-image class = "upload": quality = '. 7': url = "$ base.uploadUrl" 
 : hasApi = "false" @ upload = 'uploadImage'> </ upload-image>

How to upload image using Vuejs?

How to upload image using Vuejs?

Handling files is always a task. In this article, we will talk about how to uploads image using VueJs. We will create an images uploader that allow user to upload single or multiple images file by drag and drop or select file dialog.

Handling files is always a task. In this article, we will talk about how to uploads image using VueJs. We will create an images uploader that allow user to upload single or multiple images file by drag and drop or select file dialog.

We will then upload the selected images and display them accordingly. We will also learn to filter the upload file type, for example, we only allow images, do not allow file type like PDF.

Table of Contents

  • File Upload UI & API
  • Setup Project with Vue-Cli
  • File Upload Component
  • Style our File Upload Component
  • File Upload Component Code
  • File Upload Service
  • Display Success and Failed Result
  • Fake the Upload in Front-end
  • Bonus: Delay Your Promises
  • Summary

Sourcecode: https://github.com/chybie/file-upload-vue

Demo: https://vue-file-upload-1126b.firebaseapp.com/

File Upload UI & API

File upload consists of two parts: the UI (front-end) and the API (back-end). We will be using VueJs to handle the UI part. We need a backend application to accept the uploaded files.

Setup Project with Vue-Cli

We will be using vue-cli to scaffold Vue.js projects. We will be using the webpack-simple project template.

# install cli
npm install vue-cli -g

# then create project, with sass
# follow the instructions to install all necessary dependencies
vue init webpack-simple file-upload-vue

Alright, all set. Let's proceed to create our component.

File Upload Component

We will write our code in App.vue. Remove all the auto-generated code in the file.





  
    
      
      
        # Upload images

        
          <input type="file" multiple :name="uploadFieldName" :disabled="isSaving" @change="filesChange($event.target.name, $event.target.files); fileCount = $event.target.files.length"
            accept="image/*" class="input-file">
            
              Drag your file(s) here to begin
 or click to browse
            
            
              Uploading {{ fileCount }} files...
            
        
      
  







Notes:-

  1. Our App.vue component consists of 3 part: template (HTML), script (Javascript) and styles (SASS).
  2. Our template has an upload form.
  3. The form attribute enctype="multipart/form-data" is important. To enable file upload, this attribute must be set. Learn more about enctype here.
  4. We have a file input `` to accept file upload. The property multiple indicate it's allow multiple file upload. Remove it for single file upload.
  5. We will handle the file input change event. Whenever the file input change (someone drop or select files), we will trigger the filesChange function and pass in the control name and selected files $event.target.files, and then upload to server.
  6. We limit the file input to accept images only with the attribute accept="image/*".
  7. The file input will be disabled during upload, so user can only drop / select files again after upload complete.
  8. We capture the fileCount of the when file changes. We use the fileCount variable in displaying number of files uploading Uploading {{ fileCount }} files....
Style our File Upload Component

Now, that's the interesting part. Currently, our component look like this:

We need to transform it to look like this:

Let's style it!


...



With only few lines of scss, our component looks prettier now.

Notes:-

  1. We make the file input invisible by applying opacity: 0 style. This doesn't hide the file input, it just make it invisible.
  2. Then, we style the file input parent element, the dropbox css class. We make it look like a drop file zone surround with dash.
  3. Then, we align the text inside dropbox to center.
File Upload Component Code

Let's proceed to code our component.


...



Notes:-

  1. Our component will have a few statuses: STATUS_INITIAL, STATUS_SAVING, STATUS_SUCCESS, STATUS_FAILED, the variable name is pretty expressive themselves.
  2. Later on, we will call the Hapi.js file upload API to upload images, the API accept a field call photos. That's our file input field name.
  3. We handle the file changes with the filesChange function. FileList is an object returned by the files property of the HTML element. It allow us to access the list of files selected with the element. Learn more [here]((https://developer.mozilla.org/en/docs/Web/API/FileList).
  4. We then create a new FormData, and append all our photos files to it. FormData interface provides a way to easily construct a set of key/value pairs representing form fields and their values. Learn more here.
  5. The save function will call our file upload service (hang on, we will create the service next!). We also set the status according to the result.
  6. mount() is the vue component life cycle hook. During that point, we will set our component status to initial state.
File Upload Service

Let's proceed to create our service. We will be using axios to make HTTP calls.

Install axios

# install axios
npm install axios --save

Service

// file-upload.service.js

import * as axios from 'axios';

const BASE_URL = 'http://localhost:3001';

function upload(formData) {
    const url = `${BASE_URL}/photos/upload`;
    return axios.post(url, formData)
        // get data
        .then(x => x.data)
        // add url field
        .then(x => x.map(img => Object.assign({},
            img, { url: `${BASE_URL}/images/${img.id}` })));
}

export { upload }

Nothing much, the code is pretty expressive itself. We upload the files, wait for the result, map it accordingly.

You may run the application now with npm run dev command. Try uploading a couple of images, and it's working! (Remember to start your backend server)

Display Success and Failed Result

We can upload the files successfully now. However, there's no indication in UI. Let's update our HTML template.





  
    
      ...form...

      
      
        ## Uploaded {{ uploadedFiles.length }} file(s) successfully.

        
          [Upload again](javascript:void(0) "Upload again")
        
        
          
            ![](item.url)
          
        
      
      
      
        ## Uploaded failed.

        
          [Try again](javascript:void(0) "Try again")
        
        

{{ uploadError }}


      
    
  

Notes:-

  1. Display the uploaded image when upload successfully.
  2. Display the error message when upload failed.
Fake the Upload in Front-end

If you are lazy to start the back-end application (Hapi, Express, etc) to handle file upload. Here is a fake service to replace the file upload service.

// file-upload.fake.service.js

function upload(formData) {
    const photos = formData.getAll('photos');
    const promises = photos.map((x) => getImage(x)
        .then(img => ({
            id: img,
            originalName: x.name,
            fileName: x.name,
            url: img
        })));
    return Promise.all(promises);
}

function getImage(file) {
    return new Promise((resolve, reject) => {
        const fReader = new FileReader();
        const img = document.createElement('img');

        fReader.onload = () => {
            img.src = fReader.result;
            resolve(getBase64Image(img));
        }

        fReader.readAsDataURL(file);
    })
}

function getBase64Image(img) {
    const canvas = document.createElement('canvas');
    canvas.width = img.width;
    canvas.height = img.height;

    const ctx = canvas.getContext('2d');
    ctx.drawImage(img, 0, 0);

    const dataURL = canvas.toDataURL('image/png');

    return dataURL;
}

export { upload }

Came across this solution in this Stackoverflow post. Pretty useful. My online demo is using this service.

Basically, what the code do is read the source, draw it in canvas, and save it as data url with the canvas toDataURL function. Learn more about canvas here.

Now you can swap the real service with the fake one.


...




...

Done! Stop your backend API, refresh your browser, you should see our app is still working, calling fake service instead.

Bonus: Delay Your Promises

Sometimes, you may want to delay the promises to see the state changes. In our case, the file upload may complete too fast. Let's write a helper function for that.

// utils.js

// utils to delay promise
function wait(ms) {
    return (x) => {
        return new Promise(resolve => setTimeout(() => resolve(x), ms));
    };
}

export { wait }

Then, you can use it in your component


...



Summary

That's it. This is how you can handle file upload without using any 3rd party libraries and plugins in Vue. It isn't that hard right?

The UI (Front-end)

A simple upload multiple image component for Vuejs

A simple upload multiple image component for Vuejs

vue upload multiple image .A simple upload multiple image component for Vuejs

vue-upload-multiple-image

A simple upload multiple image component for Vuejs NPM Package

Demo!

Development (NPM / Yarn)
npm run dev
yarn dev

Install

NPM / Yarn

Install the package:

npm install vue-upload-multiple-image
yarn add vue-upload-multiple-image

Then import it in your project

main.js

import VueLazyload from 'vue-lazyload'

Vue.use(VueLazyload)
import VueUploadMultipleImage from 'vue-upload-multiple-image'

export default {
  components: {
    VueUploadMultipleImage,
  },
}

Browser global

<script src="path/to/vue.js"></script>
<script src="path/to/dist/vue-upload-multiple-image.js"></script>
Usage

You can simply view App.vue to see how to use vue-upload-multiple-image

How to use:

<vue-upload-multiple-image
      @upload-success="uploadImageSuccess"
      @before-remove="beforeRemove"
      @edit-image="editImage"
      :data-images="images"
      ></vue-upload-multiple-image>

images has the structure:

[
  {
    path: 'http://example.com/image.jpg',
    default: 1,
    highlight: 1,
    caption: 'caption to display. receive', // Optional
  }
]
Example
<template>
  <div id="my-strictly-unique-vue-upload-multiple-image" style="display: flex; justify-content: center;">
    <vue-upload-multiple-image
      @upload-success="uploadImageSuccess"
      @before-remove="beforeRemove"
      @edit-image="editImage"
      :data-images="images"
      idUpload="myIdUpload"
      editUpload="myIdEdit"
      ></vue-upload-multiple-image>
  </div>
</template>

<script>
import VueUploadMultipleImage from 'vue-upload-multiple-image'
import axios from 'axios'
export default {
  name: 'app',
  data () {
    return {
      images: []
    }
  },
  components: {
    VueUploadMultipleImage
  },
  methods: {
    uploadImageSuccess(formData, index, fileList) {
      console.log('data', formData, index, fileList)
      // Upload image api
      // axios.post('http://your-url-upload', formData).then(response => {
      //   console.log(response)
      // })
    },
    beforeRemove (index, done, fileList) {
      console.log('index', index, fileList)
      var r = confirm("remove image")
      if (r == true) {
        done()
      } else {
      }
    },
    editImage (formData, index, fileList) {
      console.log('edit data', formData, index, fileList)
    }
  }
}
</script>

<style>
#my-strictly-unique-vue-upload-multiple-image {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

h1, h2 {
  font-weight: normal;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  display: inline-block;
  margin: 0 10px;
}

a {
  color: #42b983;
}
</style>

Options

Props

name type default description
idUpload String image-upload Id of input upload
idEdit String image-edit Id of input edit
dragText String Kéo hình ảnh(nhiều) Drag Text
browseText String (hoặc) Chọn Browse Text
primaryText String Mặc định Primary Text
markIsPrimaryText String Đặt làm mặc định Set default image
popupText String Hình ảnh này sẽ được hiển thị làm mặc định Description default image
dropText String Thả tệp của bạn ở đây ... Drag and drop
accept String image/gif,image/jpeg,image/png,image/bmp,image/jpg Accept
dataImages Array [] Array images
multiple Boolean true Set upload multiple image
showPrimary Boolean true Show text default image
maxImage Number 5 Maximum upload image

Events

name arguments description
upload-success (formData, index, fileList) Upload image succcess
edit-image (formData, index, fileList) Edit image succcess
before-remove (index, done, fileList) Before delete image
mark-is-primary (index, fileList) Set default image
limit-exceeded amount Limit exceeded images when drop
Dependencies Download Details:

Author: lekhang2512

Live Demo: https://codepen.io/lekhang2512/pen/qJmQaZ

GitHub: https://github.com/lekhang2512/vue-upload-multiple-image

Top 10+ UI/UX Designing Companies Reviews

Top 10+ UI/UX Designing Companies Reviews

[TopDevelopers](https://www.topdevelopers.co/ "TopDevelopers") has listed the competent [UI/UX designers](https://www.topdevelopers.co/directory/ui-ux-designers "UI/UX designers") after an in-depth evaluation of their presence in the market...

TopDevelopers has listed the competent UI/UX designers after an in-depth evaluation of their presence in the market, customer reviews, work efficiency of the teams and their expertise in designing. The UI/UX service providers here can create highly interactive and aesthetically sound designs that will help business apps in user retention. Since UI/UX is one of the major areas for a successful website or app we have picked these companies carefully to fulfill your needs.

Here is the list of Best UI UX Designers & Developers Firms.