Dropzone Override Init and Upload Server Image
Introduction
In this article, we volition talk nearly how to handle file uploads with VueJs. We will create an images uploader that allow user to upload single or multiple images file past drag and driblet or select file dialog.
We will so upload the selected images and display them accordingly. We will too learn to filter the upload file type, for example, we just allow images, do not allow file type similar PDF.
- Sourcecode: https://github.com/chybie/file-upload-vue
- Demo: https://vue-file-upload-1126b.firebaseapp.com/
File Upload UI & API
File upload consists of 2 parts: the UI (front-end) and the API (dorsum-terminate). We will be using VueJs to handle the UI part. We need a backend application to accept the uploaded files. You may follow the backend tutorials or download and run either one of these server side application to handle file upload for your backend:-
- File upload with Hapi.js: https://scotch.io/bar-talk/handling-file-uploads-with-hapi-js, or
- File upload with Express + Multer: https://scotch.io/tutorials/express-file-uploads-with-multer, or
- Switch to any deject solution of your choice (Amazon S3, Google Drive, etc).
We will be using File upload with Hapi.js equally our backend throughout this articles. We will also learn the tricks to enable fake upload on the front-end.
Setup Project with Vue-Cli
We volition exist 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 machine-generated code in the file.
<!-- App.vue --> <!-- HTML Template --> <template > <div id = "app" > <div grade = "container" > <!--UPLOAD--> <course enctype = "multipart/form-data" novalidate five-if = "isInitial || isSaving" > <h1 > Upload images </h1 > <div class = "dropbox" > <input type = "file" multiple :name = "uploadFieldName" :disabled = "isSaving" @change = "filesChange($event.target.name, $upshot.target.files); fileCount = $issue.target.files.length" accept = "image/*" form = "input-file" > <p v-if = "isInitial" > Elevate your file(s) here to begin <br > or click to browse </p > <p v-if = "isSaving" > Uploading {{ fileCount }} files... </p > </div > </form > </div > </template > <!-- Javascript --> <script > </script > <!-- SASS styling --> <style lang = "scss" > </style >
Notes:-
- Our
App.vue
component consists of 3 part: template (HTML), script (Javascript) and styles (SASS). - Our template has an upload form.
- The class attribute
enctype="multipart/form-data"
is of import. To enable file upload, this attribute must be set. Learn more about enctype hither. - We have a file input
<input type="file" />
to take file upload. The propertymultiple
indicate it's allow multiple file upload. Remove it for unmarried file upload. - We will handle the file input
modify
issue. Whenever the file input modify (someone drop or select files), we will trigger thefilesChange
role and pass in the control proper noun and selected files$issue.target.files
, and and then upload to server. - We limit the file input to have images only with the aspect
have="image/*"
. - The file input will be disabled during upload, and then user tin only driblet / select files once again subsequently upload complete.
- We capture the
fileCount
of the when file changes. We use thefileCount
variable in displaying number of files uploadingUploading {{ fileCount }} files...
.
Manner our File Upload Component
Now, that's the interesting role. Currently, our component look like this:
Nosotros demand to transform information technology to look like this:
Allow's style it!
<!-- App.vue --> ... <!-- SASS styling --> <style lang="scss"> .dropbox { outline : 2px dashed greyness; / * the nuance box * / outline-offset : -10px; background : lightcyan; color : dimgray; padding : 10px 10px; min-height : 200px; / * minimum meridian * / position : relative; cursor : pointer; } .input-file { opacity : 0; / * invisible simply it's there! * / width : 100%; height : 200px; position : absolute; cursor : pointer; } .dropbox : hover { background : lightblue; / * when mouse over to the drib zone, change colour * / } .dropbox p { font-size : 1.2em; text-align : center; padding : 50px 0; } </manner>
With only few lines of scss, our component looks prettier now.
Notes:-
- Nosotros make the file input invisible by applying
opacity: 0
style. This doesn't hibernate the file input, it just brand it invisible. - Then, we mode the file input parent element, the
dropbox
css class. Nosotros arrive wait like a driblet file zone surround with dash. - And so, nosotros marshal the text inside dropbox to center.
File Upload Component Code
Let'south proceed to code our component.
< ! -- App.vue -- > ... < ! -- Javascript -- > <script> import { upload } from './file-upload.service' ; const STATUS_INITIAL = 0 , STATUS_SAVING = 1 , STATUS_SUCCESS = two , STATUS_FAILED = 3 ; export default { name : 'app' , information ( ) { return { uploadedFiles : [ ] , uploadError : nothing , currentStatus : aught , uploadFieldName : 'photos' } } , computed : { isInitial ( ) { render this .currentStatus === STATUS_INITIAL ; } , isSaving ( ) { return this .currentStatus === STATUS_SAVING ; } , isSuccess ( ) { render this .currentStatus === STATUS_SUCCESS ; } , isFailed ( ) { return this .currentStatus === STATUS_FAILED ; } } , methods : { reset ( ) { // reset form to initial country this .currentStatus = STATUS_INITIAL ; this .uploadedFiles = [ ] ; this .uploadError = null ; } , save ( formData ) { // upload data to the server this .currentStatus = STATUS_SAVING ; upload (formData) . then ( x => { this .uploadedFiles = [ ] . concat (x) ; this .currentStatus = STATUS_SUCCESS ; } ) . catch ( err => { this .uploadError = err.response; this .currentStatus = STATUS_FAILED ; } ) ; } , filesChange ( fieldName, fileList ) { // handle file changes const formData = new FormData ( ) ; if ( !fileList.length) render ; // append the files to FormData Array . from ( Assortment (fileList.length) . keys ( ) ) . map ( x => { formData. append (fieldName, fileList[x] , fileList[x] .proper noun) ; } ) ; // relieve it this . save (formData) ; } } , mounted ( ) { this . reset ( ) ; } , } < /script>
Notes:-
- Our component will take a few statuses: STATUS_INITIAL, STATUS_SAVING, STATUS_SUCCESS, STATUS_FAILED, the variable name is pretty expressive themselves.
- Later on, we volition phone call the Hapi.js file upload API to upload images, the API accept a field call
photos
. That's our file input field name. - We handle the file changes with the
filesChange
function.FileList
is an object returned past the files property of the HTML <input> element. Information technology allow us to admission the listing of files selected with the <input type="file"> element. Acquire more [here]((https://developer.mozilla.org/en/docs/Web/API/FileList). - We then create a new
FormData
, and suspend all ourphotos
files to it.FormData
interface provides a manner to easily construct a set of central/value pairs representing grade fields and their values. Learn more here. - The
save
function volition phone call our file upload service (hang on, we will create the service next!). Nosotros also set the status according to the result. -
mount()
is the vue component life cycle claw. During that indicate, we will gear up our component status to initial country.
File Upload Service
Allow's keep to create our service. We volition be using axios to make HTTP calls.
Install axios
# install axios npm install axios --save
Service
// file-upload.service.js import * every bit 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 => ten.data) // add together url field . then ( x => x. map ( img => Object. assign ( { } , img, { url : ` ${ BASE_URL } /images/ ${img.id} ` } ) ) ) ; } export { upload }
Zippo 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. Effort uploading a couple of images, and it'south working! (Remember to start your backend server)
Display Success and Failed Result
We can upload the files successfully now. However, in that location's no indication in UI. Allow'south update our HTML template.
<!-- App.vue --> <!-- HTML Template --> <template > <div id = "app" > <div form = "container" > ...form... <!--SUCCESS--> <div v-if = "isSuccess" > <h2 > Uploaded {{ uploadedFiles.length }} file(s) successfully. </h2 > <p > <a href = "javascript:void(0)" @click = "reset()" > Upload once again </a > </p > <ul course = "list-unstyled" > <li v-for = "item in uploadedFiles" > <img :src = "item.url" course = "img-responsive img-thumbnail" :alt = "item.originalName" > </li > </ul > </div > <!--FAILED--> <div v-if = "isFailed" > <h2 > Uploaded failed. </h2 > <p > <a href = "javascript:void(0)" @click = "reset()" > Try once again </a > </p > <pre > {{ uploadError }} </pre > </div > </div > </div > </template >
Notes:-
- Brandish the uploaded image when upload successfully.
- Display the error message when upload failed.
False the Upload in Front-end
If you are lazy to start the back-stop application (Hapi, Express, etc) to handle file upload. Here is a faux service to supplant the file upload service.
// file-upload.fake.service.js function upload ( formData ) { const photos = formData. getAll ( 'photos' ) ; const promises = photos. map ( ( ten ) => getImage (x) . then ( img => ( { id : img, originalName : 10.name, fileName : 10.name, url : img } ) ) ) ; render Promise. all (promises) ; } function getImage ( file ) { render new Hope ( ( resolve, decline ) => { const fReader = new FileReader ( ) ; const img = certificate. createElement ( 'img' ) ; fReader. onload = ( ) => { img.src = fReader.event; resolve ( getBase64Image (img) ) ; } fReader. readAsDataURL (file) ; } ) } function getBase64Image ( img ) { const sheet = document. createElement ( 'canvas' ) ; sheet.width = img.width; canvas.height = img.top; const ctx = canvas. getContext ( '2d' ) ; ctx. drawImage (img, 0 , 0 ) ; const dataURL = sail. toDataURL ( 'image/png' ) ; render dataURL; } export { upload }
Came beyond this solution in this Stackoverflow post. Pretty useful. My online demo is using this service.
Basically, what the lawmaking practise is read the source, draw it in canvass, and salvage it as data url with the sheet toDataURL
role. Acquire more nigh canvass here.
Now you can swap the real service with the fake one.
< ! -- App.vue -- > ... < ! -- Javascript -- > <script> // bandy as you need import { upload } from './file-upload.fake.service' ; // simulated service // import { upload } from './file-upload.service'; // real service < /script> ...
Done! Stop your backend API, refresh your browser, you should run across our app is still working, calling fake service instead.
Bonus: Delay Your Promises
Sometimes, you may want to delay the promises to see the country changes. In our case, the file upload may consummate too fast. Let's write a helper part for that.
// utils.js // utils to delay hope function await ( ms ) { render ( ten ) => { return new Promise ( resolve => setTimeout ( ( ) => resolve (10) , ms) ) ; } ; } export { wait }
Then, you tin can apply information technology in your component
< ! -- App.vue -- > ... < ! -- Javascript -- > <script> import { await } from './utils' ; ... salve ( formData ) { ... . upload (formData) . then ( wait ( 1500 ) ) // DEV ONLY: wait for one.5s . so ( x => { this .uploadedFiles = [ ] . concat (10) ; this .currentStatus = STATUS_SUCCESS ; } ) ... } , < /script>
Conclusion
That's information technology. This is how you tin can handle file upload without using whatsoever tertiary political party libraries and plugins in Vue. It isn't that difficult right?
Happy coding!
The UI (Front-end)
- Sourcecode: https://github.com/chybie/file-upload-vue
- Demo: https://vue-file-upload-1126b.firebaseapp.com/
The API (Dorsum-end) Tutorials and Sourcode
- File upload with Hapi.js: https://scotch.io/bar-talk/handling-file-uploads-with-hapi-js, or
- File upload with Express + Multer: https://scotch.io/tutorials/express-file-uploads-with-multer, or
- Switch to any deject solution of your selection (Amazon S3, Google Drive, etc).
strangewittappona.blogspot.com
Source: https://www.digitalocean.com/community/tutorials/how-to-handle-file-uploads-in-vue-2