vue怎么处理文件上传_如何在Vue 2中处理文件上传

vue怎么处理文件上传

In this article, we will talk about how to handle file uploads with 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.

在本文中,我们将讨论如何使用VueJs处理文件上传。 我们将创建一个图像上传器,允许用户通过拖放或选择文件对话框上传单个或多个图像文件。

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.

然后,我们将上传选定的图像并进行相应显示。 我们还将学习过滤上传文件的类型,例如,我们只允许图片,不允许像PDF这样的文件类型。

Image uploader

文件上传UI和API ( 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. You may follow the backend tutorials or download and run either one of these server side application to handle file upload for your backend:-

文件上传由两部分组成: UI(前端)API(后端) 。 我们将使用VueJs处理UI部分。 我们需要一个后端应用程序来接受上传的文件。 您可以按照后端教程进行操作,也可以下载并运行以下任一服务器端应用程序来处理后端的文件上传:-

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

在本文中,我们将使用Hapi.js的文件上传作为我们的后端。 我们还将学习在前端启用假冒上传的技巧。

使用Vue-Cli建立项目 ( 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.

我们将使用vue-cli搭建Vue.js项目。 我们将使用webpack-simplewebpack-simple项目模板。

# 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.

我们将在App.vue编写代码。 删除文件中所有自动生成的代码。

<!-- App.vue -->

<!-- HTML Template -->
<template>
  <div id="app">
    <div class="container">
      <!--UPLOAD-->
      <form enctype="multipart/form-data" novalidate v-if="isInitial || isSaving">
        <h1>Upload images</h1>
        <div class="dropbox">
          <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">
            <p v-if="isInitial">
              Drag 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:-

笔记:-

  1. Our App.vue component consists of 3 part: template (HTML), script (Javascript) and styles (SASS).

    我们的App.vue组件由3部分组成:模板(HTML),脚本(Javascript)和样式(S​​ASS)。
  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.

    表单属性enctype="multipart/form-data"很重要。 要启用文件上传,必须设置此属性。 在此处了解有关enctype的更多信息。
  4. We have a file input <input type="file" /> to accept file upload. The property multiple indicate it's allow multiple file upload. Remove it for single file upload.

    我们有一个文件输入<input type="file" />来接受文件上传。 属性multiple表示允许上传多个文件。 将其删除以上传单个文件。
  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.

    我们将处理文件输入change事件。 每当文件输入更改(有人删除或选择文件)时,我们将触发filesChange函数并传入控件名称和所选文件$event.target.files ,然后上载到服务器。
  6. We limit the file input to accept images only with the attribute accept="image/*".

    我们将文件输入限制为仅接受具有属性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....

    我们捕获文件更改时的fileCount 。 我们使用fileCount变量来显示Uploading {{ fileCount }} files...的文件数Uploading {{ fileCount }} files...

设置文件上传组件的样式 ( Style our File Upload Component )

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

现在,这是有趣的部分。 当前,我们的组件如下所示:

File upload component without styling

We need to transform it to look like this:

我们需要将其转换为如下形式:

File upload component with styling

Let's style it!

让我们来造型​​吧!

<!-- App.vue -->
...

<!-- SASS styling -->
<style lang="scss">
  .dropbox {
    outline: 2px dashed grey; /* the dash box */
    outline-offset: -10px;
    background: lightcyan;
    color: dimgray;
    padding: 10px 10px;
    min-height: 200px; /* minimum height */
    position: relative;
    cursor: pointer;
  }

  .input-file {
    opacity: 0; /* invisible but it's there! */
    width: 100%;
    height: 200px;
    position: absolute;
    cursor: pointer;
  }

  .dropbox:hover {
    background: lightblue; /* when mouse over to the drop 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.

仅用几行scss,我们的组件现在看起来更漂亮。

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.

    我们通过应用opacity: 0样式使文件输入不可见。 这不会隐藏文件输入,只会使其不可见。
  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.

    然后,我们设置文件输入父元素dropbox css类的样式。 我们使它看起来像是一个带有短划线的拖放文件区域。
  3. Then, we align the text inside dropbox to center.

    然后,我们将Dropbox中的文本对齐到中心。

文件上传组件代码 ( File Upload Component Code )

Let's proceed to code our component.

让我们继续编写组件代码。

<!-- App.vue -->
...

<!-- Javascript -->
<script>
  import { upload } from './file-upload.service';

  const STATUS_INITIAL = 0, STATUS_SAVING = 1, STATUS_SUCCESS = 2, STATUS_FAILED = 3;

  export default {
    name: 'app',
    data() {
      return {
        uploadedFiles: [],
        uploadError: null,
        currentStatus: null,
        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)
          .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) return;

        // append the files to FormData
        Array
          .from(Array(fileList.length).keys())
          .map(x => {
            formData.append(fieldName, fileList[x], fileList[x].name);
          });

        // save it
        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 name is pretty expressive themselves.

    我们的组件将具有以下几种状态:STATUS_INITIAL,STATUS_SAVING,STATUS_SUCCESS,STATUS_FAILED,变量名本身很漂亮。
  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.

    稍后,我们将调用Hapi.js文件上传 API来上传图片,该API接受现场调用photos 。 那就是我们的文件输入字段名称。
  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).

    我们使用filesChange函数处理文件更改。 FileList是HTML元素的files属性返回的对象。 它允许我们访问用元素选择的文件的列表。 在此处[( 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.

    然后,我们创建一个新的FormData ,并将所有photos文件附加到该文件。 FormData接口提供了一种轻松构造一组代表表单字段及其值的键/值对的方法。 在这里了解更多。
  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.

    save功能将调用我们的文件上传服务(继续,我们接下来将创建该服务!)。 我们还根据结果设置状态。
  6. mount() is the vue component life cycle hook. During that point, we will set our component status to initial state.

    mount()是vue组件生命周期挂钩。 在此期间,我们将组件状态设置为初始状态。

文件上传服务 ( File Upload Service )

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

让我们继续创建我们的服务。 我们将使用axios进行HTTP调用。

安装axios (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)

您现在可以使用npm run dev命令运行该应用程序。 尝试上传几个图像,它可以正常工作! (记住要启动后端服务器)

显示成功和失败的结果 ( 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.

我们现在可以成功上传文件。 但是,UI中没有指示。 让我们更新我们HTML模板。

<!-- App.vue -->

<!-- HTML Template -->
<template>
  <div id="app">
    <div class="container">
      ...form...

      <!--SUCCESS-->
      <div v-if="isSuccess">
        <h2>Uploaded {{ uploadedFiles.length }} file(s) successfully.</h2>
        <p>
          <a href="javascript:void(0)" @click="reset()">Upload again</a>
        </p>
        <ul class="list-unstyled">
          <li v-for="item in uploadedFiles">
            <img :src="item.url" class="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 again</a>
        </p>
        <pre>{{ uploadError }}</pre>
      </div>
    </div>
  </div>
</template>

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.

如果您懒于启动后端应用程序(Hapi,Express等)来处理文件上传。 这是一项伪造的服务,用来代替文件上传服务。

// 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.

在此Stackoverflow帖子中介绍了此解决方案。 很有用。 我的在线演示正在使用此服务。

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.

基本上,代码要做的是读取源代码,将其绘制在画布中,然后使用canvas toDataURL函数将其另存为数据url。 在此处了解有关画布的更多信息。

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

现在,您可以将真实服务与假服务交换。

<!-- App.vue -->
...

<!-- Javascript -->
<script>
  // swap as you need
  import { upload } from './file-upload.fake.service'; // fake service
  // import { upload } from './file-upload.service';   // real service
</script>

...

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

做完了! 停止后端API,刷新浏览器,您应该会看到我们的应用程序仍在运行,请转而使用假服务。

奖励:延迟您的承诺 ( 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

然后,您可以在组件中使用它

<!-- App.vue -->
...

<!-- Javascript -->
<script>
  import { wait } from './utils';
  ...

  save(formData) {
     ....

        upload(formData)
          .then(wait(1500)) // DEV ONLY: wait for 1.5s 
          .then(x => {
            this.uploadedFiles = [].concat(x);
            this.currentStatus = STATUS_SUCCESS;
          })
         ...

      },
</script>

摘要 ( 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?

而已。 这样就可以在不使用Vue中任何第三方库和插件的情况下处理文件上传。 这不是很难吗?

Happy coding!

编码愉快!

The UI (Front-end)

用户界面(前端)

The API (Back-end) Tutorials and Sourcode

API(后端)教程和源代码

翻译自: https://scotch.io/tutorials/how-to-handle-file-uploads-in-vue-2

vue怎么处理文件上传

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值