Javascript required
Skip to content Skip to sidebar Skip to footer

Atl There Was a Problem Accessing the File to Upload

Introduction

In this article, we volition talk about how to handle file uploads with VueJs. We volition create an images uploader that allow user to upload single or multiple images file by drag and drop or select file dialog.

Nosotros volition then upload the selected images and display them appropriately. We will as well learn to filter the upload file blazon, for instance, nosotros just let images, do not allow file type like PDF.

Image uploader

  • 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-stop) and the API (back-cease). We will be using VueJs to handle the UI role. 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/limited-file-uploads-with-multer, or
  • Switch to any deject solution of your choice (Amazon S3, Google Bulldoze, etc).

Nosotros will be using File upload with Hapi.js equally our backend throughout this articles. We will also larn the tricks to enable fake upload on the front-end.

Setup Project with Vue-Cli

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

                      # install cli            npm            install            vue-cli -grand            # so create project, with sass            # follow the instructions to install all necessary dependencies            vue init webpack-elementary file-upload-vue                  

Alright, all set. Let'southward go along to create our component.

File Upload Component

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

                      <!-- App.vue -->            <!-- HTML Template -->                                          <template              >                                                      <div              id                              =                "app"                            >                                                      <div              form                              =                "container"                            >                        <!--UPLOAD-->                                          <form              enctype                              =                "multipart/form-data"                            novalidate              5-if                              =                "isInitial || isSaving"                            >                                                      <h1              >            Upload images                              </h1              >                                                      <div              class                              =                "dropbox"                            >                                                      <input              type                              =                "file"                            multiple              :name                              =                "uploadFieldName"                            :disabled                              =                "isSaving"                            @alter                              =                "filesChange($outcome.target.proper name, $event.target.files); fileCount = $event.target.files.length"                            accept                              =                "image/*"                            class                              =                "input-file"                            >                                                      <p              five-if                              =                "isInitial"                            >                        Drag your file(s) here to begin                              <br              >                        or click to scan                                          </p              >                                                      <p              v-if                              =                "isSaving"                            >                        Uploading {{ fileCount }} files...                                          </p              >                                                      </div              >                                                      </grade              >                                                      </div              >                                                      </template              >                        <!-- Javascript -->                                          <script              >                                                                                                          </script              >                        <!-- SASS styling -->                                          <style              lang                              =                "scss"                            >                                                                                                          </style              >                              

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/class-data" is of import. To enable file upload, this attribute must exist set. Larn more than about enctype here.
  4. We have a file input <input type="file" /> to accept file upload. The property multiple indicate it'due south allow multiple file upload. Remove it for single file upload.
  5. We will handle the file input change issue. Whenever the file input change (someone drop or select files), we will trigger the filesChange role and pass in the control name and selected files $event.target.files, and so upload to server.
  6. We limit the file input to accept images but with the attribute have="image/*".
  7. The file input will exist disabled during upload, and so user tin but drib / select files again after upload complete.
  8. We capture the fileCount of the when file changes. We apply the fileCount variable in displaying number of files uploading Uploading {{ fileCount }} files....

Way our File Upload Component

Now, that's the interesting office. Currently, our component expect similar this:

File upload component without styling

We need to transform it to look similar this:

File upload component with styling

Let's style it!

                      <!-- App.vue -->            ...            <!-- SASS styling -->            <way lang="scss">            .dropbox {                          outline              :              2px dashed grey;              /              *              the dash box              *              /                                      outline-first              :              -10px;                          background              :              lightcyan;                          color              :              dimgray;                          padding              :              10px 10px;                          min-height              :              200px;              /              *              minimum height              *              /                                      position              :              relative;                          cursor              :              pointer;            }            .input-file {                          opacity              :              0;              /              *              invisible only it's there!              *              /                                      width              :              100%;                          top              :              200px;                          position              :              accented;                          cursor              :              pointer;            }                          .dropbox              :              hover              {                          groundwork              :              lightblue;              /              *              when mouse over to the driblet zone, change color              *              /                        }            .dropbox p {                          font-size              :              1.2em;                          text-align              :              center;                          padding              :              50px 0;            }            </style>                  

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

Notes:-

  1. Nosotros make the file input invisible by applying opacity: 0 style. This doesn't hide the file input, it just brand it invisible.
  2. Then, we fashion the file input parent element, the dropbox css class. We make it look like a drib file zone surround with dash.
  3. And then, we align the text inside dropbox to center.

File Upload Component Lawmaking

Let'south go along to code our component.

                      <            !            --            App.vue            --            >            ...            <            !            --            Javascript            --            >            <script>            import            {            upload            }            from            './file-upload.service'            ;            const            STATUS_INITIAL            =            0            ,            STATUS_SAVING            =            one            ,            STATUS_SUCCESS            =            2            ,            STATUS_FAILED            =            3            ;            export            default            {            name            :            'app'            ,            information            (            )            {            render            {            uploadedFiles            :            [            ]            ,            uploadError            :            cipher            ,            currentStatus            :            zippo            ,            uploadFieldName            :            'photos'            }            }            ,            computed            :            {            isInitial            (            )            {            return            this            .currentStatus            ===            STATUS_INITIAL            ;            }            ,            isSaving            (            )            {            return            this            .currentStatus            ===            STATUS_SAVING            ;            }            ,            isSuccess            (            )            {            return            this            .currentStatus            ===            STATUS_SUCCESS            ;            }            ,            isFailed            (            )            {            return            this            .currentStatus            ===            STATUS_FAILED            ;            }            }            ,            methods            :            {            reset            (            )            {            // reset form to initial state            this            .currentStatus            =            STATUS_INITIAL            ;            this            .uploadedFiles            =            [            ]            ;            this            .uploadError            =            null            ;            }            ,            save            (            formData            )            {            // upload data to the server            this            .currentStatus            =            STATUS_SAVING            ;            upload            (formData)            .            so            (            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)            return            ;            // append the files to FormData            Array            .            from            (            Assortment            (fileList.length)            .            keys            (            )            )            .            map            (            10            =>            {            formData.            append            (fieldName,            fileList[10]            ,            fileList[x]            .name)            ;            }            )            ;            // salve information technology            this            .            save            (formData)            ;            }            }            ,            mounted            (            )            {            this            .            reset            (            )            ;            }            ,            }            <            /script>                  

Notes:-

  1. Our component will have a few statuses: STATUS_INITIAL, STATUS_SAVING, STATUS_SUCCESS, STATUS_FAILED, the variable proper noun is pretty expressive themselves.
  2. Subsequently, 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 part. FileList is an object returned by the files property of the HTML <input> element. It allow us to access the list of files selected with the <input type="file"> element. Learn more than [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 fashion to hands construct a ready of fundamental/value pairs representing form fields and their values. Learn more here.
  5. The save function will call our file upload service (hang on, nosotros will create the service side by side!). We likewise set the status according to the issue.
  6. mount() is the vue component life cycle hook. During that bespeak, we will prepare our component condition to initial state.

File Upload Service

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

Install axios

                      # install axios            npm            install            axios --relieve                  

Service

                      // file-upload.service.js            import            *            as            axios            from            'axios'            ;            const            BASE_URL            =            'http://localhost:3001'            ;            office            upload            (            formData            )            {            const            url            =                          `                              ${                BASE_URL                }                            /photos/upload              `                        ;            return            axios.            postal service            (url,            formData)            // get data            .            and then            (            x            =>            x.data)            // add url field            .            then            (            x            =>            x.            map            (            img            =>            Object.            assign            (            {            }            ,            img,            {            url            :                          `                              ${                BASE_URL                }                            /images/                              ${img.id}                            `                        }            )            )            )            ;            }            export            {            upload            }                  

Naught much, the code is pretty expressive itself. Nosotros upload the files, wait for the consequence, map it appropriately.

You may run the application now with npm run dev command. Try uploading a couple of images, and it's working! (Remember to starting time 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.

                      <!-- App.vue -->            <!-- HTML Template -->                                          <template              >                                                      <div              id                              =                "app"                            >                                                      <div              class                              =                "container"                            >                        ...grade...            <!--SUCCESS-->                                          <div              v-if                              =                "isSuccess"                            >                                                      <h2              >            Uploaded {{ uploadedFiles.length }} file(southward) successfully.                              </h2              >                                                      <p              >                                                      <a              href                              =                "javascript:void(0)"                            @click                              =                "reset()"                            >            Upload once more                              </a              >                                                      </p              >                                                      <ul              class                              =                "listing-unstyled"                            >                                                      <li              v-for                              =                "particular in uploadedFiles"                            >                                                      <img              :src                              =                "detail.url"                            class                              =                "img-responsive img-thumbnail"                            :alt                              =                "item.originalName"                            >                                                      </li              >                                                      </ul              >                                                      </div              >                        <!--FAILED-->                                          <div              five-if                              =                "isFailed"                            >                                                      <h2              >            Uploaded failed.                              </h2              >                                                      <p              >                                                      <a              href                              =                "javascript:void(0)"                            @click                              =                "reset()"                            >            Endeavour again                              </a              >                                                      </p              >                                                      <pre              >            {{ uploadError }}                              </pre              >                                                      </div              >                                                      </div              >                                                      </div              >                                                      </template              >                              

Notes:-

  1. Brandish the uploaded prototype when upload successfully.
  2. Display the error message when upload failed.

Faux the Upload in Front-end

If you are lazy to get-go the back-end awarding (Hapi, Limited, etc) to handle file upload. Hither is a false 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            Hope.            all            (promises)            ;            }            function            getImage            (            file            )            {            return            new            Promise            (            (            resolve,              reject            )            =>            {            const            fReader            =            new            FileReader            (            )            ;            const            img            =            document.            createElement            (            'img'            )            ;            fReader.            onload            =            (            )            =>            {            img.src            =            fReader.outcome;            resolve            (            getBase64Image            (img)            )            ;            }            fReader.            readAsDataURL            (file)            ;            }            )            }            office            getBase64Image            (            img            )            {            const            sail            =            document.            createElement            (            'canvas'            )            ;            canvas.width            =            img.width;            sheet.pinnacle            =            img.height;            const            ctx            =            canvas.            getContext            (            '2nd'            )            ;            ctx.            drawImage            (img,            0            ,            0            )            ;            const            dataURL            =            canvas.            toDataURL            (            'paradigm/png'            )            ;            return            dataURL;            }            consign            {            upload            }                  

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

Basically, what the code do is read the source, describe information technology in sail, and salvage it equally data url with the sail toDataURL function. Learn more virtually canvass here.

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

                      <            !            --            App.vue            --            >            ...            <            !            --            Javascript            --            >            <script>            // swap as y'all need            import            {            upload            }            from            './file-upload.fake.service'            ;            // imitation service            // import { upload } from './file-upload.service';   // real service            <            /script>            ...                  

Done! Finish your backend API, refresh your browser, you should see our app is yet working, calling faux service instead.

Bonus: Delay Your Promises

Sometimes, you lot may desire to filibuster the promises to run into the state changes. In our case, the file upload may complete as well fast. Let's write a helper part for that.

                      // utils.js            // utils to filibuster hope            role            wait            (            ms            )            {            render            (            x            )            =>            {            return            new            Promise            (            resolve            =>            setTimeout            (            (            )            =>            resolve            (x)            ,            ms)            )            ;            }            ;            }            export            {            wait            }                  

Then, you can use information technology in your component

                      <            !            --            App.vue            --            >            ...            <            !            --            Javascript            --            >            <script>            import            {            await            }            from            './utils'            ;            ...            save            (            formData            )            {            ...            .            upload            (formData)            .            so            (            wait            (            1500            )            )            // DEV Simply: await for 1.5s                        .            then            (            x            =>            {            this            .uploadedFiles            =            [            ]            .            concat            (10)            ;            this            .currentStatus            =            STATUS_SUCCESS            ;            }            )            ...            }            ,            <            /script>                  

Conclusion

That'due south it. This is how yous can handle file upload without using any third party libraries and plugins in Vue. It isn't that difficult right?

Happy coding!

The UI (Front-cease)

  • Sourcecode: https://github.com/chybie/file-upload-vue
  • Demo: https://vue-file-upload-1126b.firebaseapp.com/

The API (Back-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 whatever cloud solution of your selection (Amazon S3, Google Drive, etc).

vrolandfinced.blogspot.com

Source: https://www.digitalocean.com/community/tutorials/how-to-handle-file-uploads-in-vue-2