Vue.js如何从响应式代理对象中提取File类型文件并实现正常上传至服务器
你分析得完全正确!这个问题确实是Vue的响应式代理机制搞的鬼:当你把File对象放进Vue的响应式data中时,它会被自动包装成Proxy代理对象,而FormData.append()只认原生的Blob/File实例,遇到代理对象就会直接调用它的toString()方法,最终变成"[object File]"字符串,导致文件无法正常上传。
下面给你几种可行的解决方案:
方法1:直接传递原生File对象(最推荐)
其实你在FileForm组件里已经拿到了原生的File对象——event.target.files[0],只要直接把这个原生对象emit出去,而不是包成数组,就能从根源上避免代理问题:
修改FileForm组件的fileUpload方法:
fileUpload: function(event) { // 直接传递原生File对象,不要用数组包裹 this.$emit("upload", event.target.files[0]) }
然后在页面的upload_model方法里直接赋值:
upload_model: function(file) { this.files['Model'] = file }
这样this.files['Model']存储的就是原生的File对象,FormData.append()就能正常识别并处理了。
方法2:从代理对象中提取原生File对象
如果因为业务限制必须传递代理对象,可以使用Vue提供的toRaw()方法来获取被代理的原始对象:
首先在页面组件中导入toRaw:
import { toRaw } from 'vue'
然后在submit_model方法中提取原生File对象:
submit_model: function() { let formData = new FormData(); // 用toRaw提取代理内部的原生File对象 const rawFile = toRaw(this.files['Model']) formData.append('Model', rawFile); axios.post('http://' + document.location.host + '/api/upload_model', formData, { headers: { 'Content-Type': 'multipart/form-data' }, }) }
注意:如果你的项目用的是Vue 2.x,没有
toRaw方法,可以尝试直接访问this.files['Model'].__v_raw(但这是Vue内部属性,未来版本可能会变动,不推荐长期使用)。
方法3:避免将File对象加入响应式数据
另一种思路是:File对象本身不需要响应式更新,完全可以用一个普通变量来存储,避开Vue的响应式代理机制:
修改页面组件的代码:
data: function() { return { // 移除响应式的files对象 } }, // 新增普通变量存储文件,不加入响应式系统 modelFile: null, methods: { upload_model: function(file) { this.modelFile = file }, submit_model: function() { let formData = new FormData(); formData.append('Model', this.modelFile); axios.post('http://' + document.location.host + '/api/upload_model', formData, { headers: { 'Content-Type': 'multipart/form-data' }, }) } }
这样就完全不会触发Vue的代理包装,直接用原生File对象上传即可。
内容的提问来源于stack exchange,提问作者Dmitry

