VUE学习之Element-ui文件上传实例详解

   2023-03-08 学习力0
核心提示:目录引言单文件上传和表单一起上传需求:需同时给后端传文件FormData和其他所需参数数据的编码方式补充:代理的使用什么是代理?为什么要用代理代理的使用process.env.NODE_ENV总结引言对于文件上传,在开发主要涉及到以下两个方面:单个文件上传和表单一起实

引言

对于文件上传,在开发主要涉及到以下两个方面:

单个文件上传和表单一起实现上传(这种情况一般都是文件上传之后,后端返回保存在服务器的文件名,最后和我们的表单一起上传)

单文件上传

element-ui中的el-upload组件默认发送post请求,在使用upload组件自动携带的请求方式发送。因此详情可参考elenent-ui官网 Element-UI官网

以下使用手动上传着重介绍一下el-upload的一部分属性参数

<el-upload
  class="upload-demo"
  ref="upload"
  action="https://jsonplaceholder.typicode.com/posts/"
  :on-preview="handlePreview"
  :on-remove="handleRemove"
  :auto-upload="false"
  list-type="picture">
  <el-button slot="trigger" size="small" type="primary">选取文件</el-button>
  <el-button style="margin-left: 10px;" size="small" type="success" @click="submitUpload">上传到服务器</el-button>
  <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
</el-upload>
<script>
  export default {
    data() {
      return {
        fileList: []
      };
    },
    methods: {
      handleRemove(file, fileList) {
        console.log(file, fileList);
      },
      handlePreview(file) {
        console.log(file);
      },
      submitUpload() {
        this.$refs.upload.submit();
      },
    }
  }
</script>
  • data 上传时可携带的参数,这里后台用map接收
  • file-list 上传的文件列表, 例如: [{name: ‘food.jpg’, url: ‘https://xxx.cdn.com/xxx.jpg’}]
  • on-success 上传成功之后的钩子,可以用来清除表单校验和文件列表
  • on-error 上传失败之后的钩子
  • auto-upload 是否在选取文件后立即进行上传,默认为true,会自动上传,false取消自动上传
  • list-type 文件列表的类型 text/picture/picture-card 默认text
  • on-preview 点击文件列表中已上传的文件时的钩子
  • on-change 文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用
  • slot=“trigger” 触发文件选择框的内容
  • slot=“tip” 提示说明文字

和表单一起上传

需求:需同时给后端传文件FormData和其他所需参数

实现方法一:

关键点:el-upload的http-request方法获取文件File信息和FormData方式传参

http-request :覆盖默认的上传行为,可以自定义上传的实现;函数的参数里包含file对象filename字段action字段,以及onProgress,onSuccess,onError三个函数,可以在自定义上传中,在不同时机调用这些函数,会触发upload组件传入的上传钩子函数

<!-- 文件上传-->
<el-form :rules="rules" :model="dataForm" ref="dataForm" label-width="150px" @submit.native.prevent>
      <el-form-item label="名称:" prop="name">
        <el-input type="text" v-model.trim="dataForm.name" clearable></el-input>
      </el-form-item>
      <el-form-item label="文件:" prop="file" >
        <el-upload
            class="upload-import"
            ref="uploadImport"
            :http-request="httpRequest"
            action=""
            :on-remove="handleRemove"
            :file-list="fileList"
            :limit="1" <!-- 限制上传数量-->
            :on-change="handleChange"
            :auto-upload="false"	<!-- 关闭自动上传-->
            accept="application/zip,.zip">  <!-- 设置接收的文件类型-->
          <el-button v-show="!hasFile" slot="trigger" size="small" type="primary" >选取文件</el-button>
          <div slot="tip" class="el-upload__tip">只能上传zip文件,且不超过10M</div>
        </el-upload>
      </el-form-item>
      <el-form-item>
         <el-button type="primary" @click="submitUpload">提交</el-button>
         <el-button @click="resetForm('ruleForm')">重置</el-button>
         <el-button @click="dialogFormVisible = false">取 消</el-button>
      </el-form-item>
 </el-form>

<script>
export default {
	data(){
		return {
			dataForm: {
	          name: '',
	          file: null
	        },
	        rules: {
	          name: [
	            {required: true, message: "请输入名称", trigger: "blur"},
	            {max: 50, message: "最长可输入50个字符", trigger: "blur"},
	            {validator: isvalidatName, trigger: "blur" },
	          ],
	          file: [
	            {required: true, message: "请选择上传文件", trigger: "blur"},
	          ]
        },
        hasFile: false,
        fileList: []
	},
	methods: {
		 handleRemove(file, fileList) {
	        if (!fileList.length) {
	          this.hasFile = false;
	        }
	        this.dataForm.file = null;
	      },
	      handleChange(file, fileList) {
    	    if (fileList.length >= 2) {
	          return;
	        }
	        if (fileList.length === 1) {
	          this.hasFile = true;
	        }
	        this.dataForm.file = file;
	      },
	      //确定按钮
	      //调用组件upload的submit方法,从而触发httpRequest方法,实现手动上传
	      submitUpload() {
	      	this.$refs.dataForm.validate(valid => {
	      		if(vaild){
	      			this.$refs.uploadImport.submit();
	      		}
			})
    	  },
    	  httpRequest(param) {
	    	  let fd = new FormData();
		      fd.append('file', param.file); // 传文件
		      fd.append('name',  dataForm.name); 
		      //dataPar.file.raw
		      axios.post('/interface/importProject', fd , {
				headers: {'Content-Type': 'multipart/form-data'},//定义内容格式,很重要
				timeout: 20000,
			  }).then(response => {
				console.log(res)
			    //接口成功调用params上的onSuccess函数,会触发默认的successHandler函数
          		//这样可以用自带的ui等
          		///params.onSuccess({name: 'eric'})
			    }).catch(err =>{})						
		      }
			})
   		 }
	}
}
</script>

实现方法二:

关键点:把表单数据作为上传时附带的额外参数提交给后台就好了。

<el-dialog title="添加歌曲" :visible.sync="dialogFormVisible">
   <el-form :model="form" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
     <el-form-item label="曲名" prop="name">
       <el-input v-model="ruleForm.name"></el-input>
     </el-form-item>
     <el-form-item label="专辑" prop="introduction">
       <el-input  v-model="ruleForm.introduction"></el-input>
     </el-form-item>
     <el-form-item label="歌词" prop="lyric">
       <el-input type="textarea" v-model="ruleForm.lyric"></el-input>
     </el-form-item>
     <el-form-item label="歌曲文件" prop="introduction">
       <el-upload
		   ref="upload"
		   :action="url" 
		   :limit="2"
		   multiple
		   :with-credentials="true"
		   :on-success="upFile"
		   :on-remove="handleRemove"
		   :on-exceed="handleExceed"
		   :data="upData"
		   :auto-upload="false"
		   accept=".KEY, .CRT">
		   <img src="/static/images/web/upload.png" alt>
		    <span>选择上传文件</span>
		   <div slot="tip" class="el-upload__tip">只能上传.key/.crt文件,且只能上传两个</div>
		 </el-upload>
     </el-form-item>
     <el-form-item>
       <el-button type="primary" @click="submitUpload">提交</el-button>
       <el-button @click="resetForm('ruleForm')">重置</el-button>
       <el-button @click="dialogFormVisible = false">取 消</el-button>
     </el-form-item>
   </el-form>
</el-dialog>
data() {
    return {
      url: service.defaults.baseURL + "aaa/bbb", // 这里写上传文件的地址
      form: {
        name: "",
        introduction: "",
        lyric: "",
      }
    };
  },
computed: {
   // 这里定义上传文件时携带的参数,即表单数据
    upData: function() {
      return {
        body: JSON.stringify(this.form)
      }
    }
  },
methods: {
  add(form) {
    this.$refs[form].validate(async valid => {
      if (valid) {
      // 表单验证通过后使用组件自带的方法触发上传事件
        this.$refs.upload.submit()
      } else {
        return false;
      }
    });
},
  // 成功上传文件
  upFile(res, file) {
    if (res.status == 200) {
      // 文件上传成功后的回调,比如一些提示信息或者页面跳转都写在这里
      this.$message.success(res.info);
    } else {
      this.$message.warning(res.info);
      let _this = this;
      setTimeout(function() {
        _this.$refs.upload.clearFiles();
      }, 1000);
    }
  },
 // 上传文件超出个数
  handleExceed(files, fileList) {
    this.$message.warning(`当前只能选择上传2 个证书`);
  },
 //  移除文件
 handleRemove(res, file, fileList) {
   this.$message.warning(`移除当前${res.name}证书,请重新选择证书上传!`);
 }
}

因为文件上传使用的是Content-Type: multipart/form-data;携带参数使用的是FormData格式,我们需要把表单数据转换一下格式,后台才能接收到。

VUE学习之Element-ui文件上传实例详解

将body的格式进行转换一下:

  • JSON.stringify() 方法将一个 JavaScript 对象或值转换为 JSON 字符串
  • JSON.parse()可以将JSON字符串转为一个对象。

VUE学习之Element-ui文件上传实例详解

VUE学习之Element-ui文件上传实例详解

数据的编码方式

在此之前可以先了解一下文件上传的编码方式

http协议规定 POST 提交的数据必须放在消息主体(entity-body)中,但协议并没有规定数据必须使用什么编码方式。实际上,开发者完全可以自己决定消息主体的格式,只要最后发送的 HTTP 请求满足上面的格式就可以。

但是,数据发送出去,还要服务端解析成功才有意义。一般服务端语言如 php、python ,java等,以及它们的 framework,都内置了自动解析常见数据格式的功能。服务端通常是根据请求头(headers)中的 Content-Type 字段来获知请求中的消息主体是用何种方式编码,再对主体进行解析。

前端HTTP发POST请求携带参数与后端接口接收参数

补充:代理的使用

action:代表上传的地址,在开发中一般写成动态绑定的属性,在计算属性中决定其最终值。

<el-upload
  :action="uploadURL">
</el-upload>
<script>
  export default {
    data() {
      return {
        uploadURL: '',
      };
    },
    computed: {
	    uploadURL() {
	      if(process.env.NODE_ENV === 'production'){
	        return '/api/uploadFile'
	      }
	      return '/dev-api/api/uploadFile'
	    }
  	}
  }
</script>

为什么要区分运行环境来使用不同的url?因为开发环境配置了代理

 proxy: {
    [REQUEST_PRE]: {
       target: 'http://localhost:88',
       changeOrigin: true,
       secure: false,
       pathRewrite: {
         ['^' + process.env.VUE_APP_BASE_API]: ''
       }
    },    
 }

而这个值在开发环境中是"/dev-api",在生产环境是“/”

所以开发环境需要在开头加上"/dev-api",生产环境则不需要

那为什么只有这一个要单独处理,其他的url不需要呢?因为其他的url是通过axios请求的,axios中配置了baseUrl,所以其他的url不需要单独处理了

什么是代理?为什么要用代理

因为开发环境是配置了代理的,代理就是通过一个特殊的网络服务去访问另一个网络服务的一种间接访问方式。像我们不能直接访问国外的网站,只能使用VPN,就是是使用了代理。

那么前端为什么要代理?

  1. 前端应用的要能访问,那必须是放在服务器上,(服务器可以是nginx、node.js、apache、tomcat等),我们本地vue开发就是用nodejs启动了一个服务。
  2. 由于浏览器的同源策略(协议,IP,端口号都相同为同源),禁止网站向非同源的服务器发送ajax异步请求,也就是跨域。

因此使用代理来解决跨域问题

VUE学习之Element-ui文件上传实例详解

代理的使用

Vue代理配置

vuecli3的代理设置在vue.config.js->devServer->proxy

vite构建的vue代理设置在 vite.config.js->server->proxy

(Vite 是 vue 的作者尤雨溪在开发 vue3.0 的时候开发的一个 基于原生 ES-Module 的前端构建工具)

他们的代理配置是一样的,这里以vuecli3为例:

const REQUEST_PRE = "/api"
module.exports = {
	// 其他配置省略
	
	// 本地代理配置
	devServer: {
		// 默认是false,可以将http://localhost:8080 变为 https://localhost:8080
        https: true,
        // 代理配置
        proxy: {
        	// 简单写法
        	"/normal":{
        		target: 'http://localhost:80',
        		ws: true,  // 允许websocket代理
                changeOrigin: true //允许跨域
        	},
        	
        	// 变量写法
            [REQUEST_PRE]: {
                target: 'http://localhost:88',
                ws: true,
                changeOrigin: true
            },
            
            // 重写,当访问http://localhost:80/req/getData
            // 实际上访问的是 http://localhost:8888/module/getData
            '/req': {
                target: 'http://localhost:8888',
                pathRewrite: path => path.replace('/req', '/module'),//replace方法返回一个替换好的新的字符串
                ws: true,
                changeOrigin: true
            },
        },
        open: true
    }
}

注意(坑点):

1、后面的反斜杠要保持一致,虽然对接口调用没有影响,但是对代理文件的相对路径有影响

如 /req,target就写 http://localhost:8888

如 /req/,target就写 http://localhost:8888/

2、不管是vuecli还是vite,同前缀代理是有前后顺序的,只生效前面的

比如当你调用 localhost:8080/api/other/getData

下面的写法会代理到 8888

'/api': 'http://localhost:8888',
'/api/other': 'http://localhost:88'

下面的写法会代理到 88

'/api/other': 'http://localhost:88',
'/api': 'http://localhost:8888'

接口调用

const REQUEST_PRE = '/api'
axios.get({
	url: `${REQUEST_PRE}/getData`
}).then(res=>{
	console.log(res)
})

3、pathRewrite:对象/函数,重写目标的 url 路径。对象键将用作正则表达式来匹配路径。

// 重写路径
pathRewrite: { '^/old/api' : '/new/api' }

// 删除路径
pathRewrite: { '^/remove/api' : '' }

// 添加基本路径
pathRewrite: { '^/' : '/basepath/' }

// 自定义重写
pathRewrite: function  ( path ,  req )  {  return  path . 替换('/api' , '/base/api' ) }

// 自定义重写,返回 Promise 
pathRewrite: async  function  ( path ,  req )  { 
  const  should_add_something  =  await  httpRequestToDecideSomething ( path ) ; 
  if  ( should_add_something )  path  +=  "something" ; 
  返回 路径;
}

Proxy文档参考地址

process.env.NODE_ENV

  1. process 是 node 的全局变量,并且 process 有 env 这个属性,但是没有 NODE_ENV 这个属性
  2. NODE_ENV这个变量并不是 process.env 直接就有的,而是通过设置得到的。这个变量的作用是:我们可以通过判断这个变量区分开发环境或生产环境。
  3. 当我们设置 mode 为 development 或者 production时,webpack会自动的进行一些设置
mode: development --> process.env.NODE_ENV = development
mode: production --> process.env.NODE_ENV = production

默认情况下 --> process.env.NODE_ENV = production

总结

原文地址:https://blog.csdn.net/weixin_48242257/article/details/124143182
 
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • vue3+TS 自定义指令:长按触发绑定的函数
    vue3+TS 自定义指令:长按触发绑定的函数而然间看到一个在vue2中写的长按触发事件的自定义指定,想着能不能把他copy到我的vue3项目中呢。编写自定义指令时遇到的几个难点1.自定义指令的类型在ts中写任何东西都要考虑到类型的问题,自定义指令的类型问题依然存
    03-08
  • Vue生命周期 vue生命周期几个阶段
    Vue生命周期 vue生命周期几个阶段
    官网解释一、Vue的生命周期Vue 实例有⼀个完整的⽣命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom - 渲染、更新 - 渲染、卸载 等⼀系列过程,称这是Vue的⽣命周期。Vue2生命周期:1.beforeCreate(创建前)数据观测和初始化事件还未开始,此时 dat
    03-08
  • vue 中安装并使用echart
    vue 中安装并使用echart
    本文为博主原创,转载请注明出处:1.安装echart 依赖:  安装命令: npm install echarts --save  在vscode 的终端窗口进行执行,如图所示:   执行完之后,查看 项目中的echart 版本依赖是否添加成功:  package-lock.json 中有具体的echart 依赖
    03-08
  • day04-Vue01
    day04-Vue01
    Vue011.Vue是什么?Vue(读音/vju:/,类似于view)是一个前端框架,依据构建用户界面Vue的核心库只关注视图层,不仅易于上手,还便于与第三方库或者项目整合支持和其他类库结合使用开发复杂的单页应用非常方便Vue是Vue.js的简称官网:Vue.js - 渐进式 JavaScr
    03-08
  • Vue + Element 自定义上传封面组件
    Vue + Element 自定义上传封面组件
    前一段时间做项目,频繁使用到上传图片组件,而且只上传一个封面,于是想着自定义一个图片封面上传组件。先来看一下效果:                      第一张图片是上传之前,第二张图片是上传成功后,第3张图片是鼠标放上去之后的效果! 首先整理需
    03-08
  • 基于ZR.VUE 前端的改造,页面刷新报错
     问题描述:前后端分离开发,分开部署. 页面刷新 直接报404 错误的解决办法提示:  先在 .env.development 中 配置 VUE_APP_BASE_API , 将 '/' 替换为 后端地址 'http://localhost:8888/'如果是对应的发布的正式环境,也要修改  .env.production 的VUE_APP_
    03-08
  • Vue3 企业级优雅实战 - 组件库框架 - 9 实现组
    上文搭建了组件库 cli 的基础架子,实现了创建组件时的用户交互,但遗留了 cli/src/command/create-component.ts 中的 createNewComponent 函数,该函数要实现的功能就是上文开篇提到的 —— 创建一个组件的完整步骤。本文咱们就依次实现那些步骤。(友情提示
    03-08
  • vue-3 this概念
    一、this概念官方是这样说的:在 setup()内部,this 不会是该活跃实例的引用因为 setup() 是在解析其它组件选项之前被调用的,所以 setup() 内部的 this 的行为与其它选项中的 this 完全不同这在和其它选项式 API 一起使用 setup() 时可能会导致混淆啥意思呢
    03-08
  • vue3和百度地图关键字检索 定位 点击定位
    vue3和百度地图关键字检索 定位 点击定位
    效果图在index.html中引入百度地图开放平台  去申请你的ak 非常的简单可以自己百度 一下!-- 这个用官网给的有好多警告 更具百度的把 https://api.map.baidu.com/getscript?v=2.0ak=xxxxxxxxxxxxxxxxxxxxx 换为这个就没有那么多 报错了 --scripttype="text/j
    03-08
  • Vue集成lodop插件实现打印功能 vue打印console
    Vue集成lodop插件实现打印功能 vue打印console
    目录VUE简单使用lodop1.创建LodopFuncs.js文件2.在打印功能vue页面引入LodopFuncs3.执行打印方法4.打印接口函数官网样例说明5.完整页面示例VUE 集成LODOP插件打印Lodop、C-Lodop使用说明及样例http://www.lodop.net/LodopDemo.htmlVUE简单使用lodop1.创建Lodo
    03-08
点击排行