我有一个通用的头像预览模式:
<avatar-update :img-file="avatarFile" :show="avatarModalShow" :img-url="url" @close="avatarModalShow = !avatarModalShow" :change-avatar="updateCrop" @destroyUrl="imgUrl = null"> </avatar-update>
提交头像后,我用我的根将一堆属性发送到AvatarUpdate组件。
HTML
<div>
<label for="avatar" class="cursor-pointer thumbnail-link bg-white p-1 rounded-lg" href="">
<img class="thumbnail" src="{{asset('images/avatars/avatar-1.png')}}">
</label>
<input id="avatar" class="hidden" type="file" @change="onFileChange"/>
</div>
根
onFileChange: function(e) {
const file = e.target.files[0];
this.url = URL.createObjectURL(file);
this.updateCrop = !this.updateCrop;
this.avatarModalShow = !this.avatarModalShow;
this.avatarFile = file;
},
当我在onFileChange函数中console.log文件const时,我得到了文件对象。但是,当我尝试在AvatarUpdate组件中输出{{imgFile}}
属性时,我得到了一个空对象。
我想知道这是否安全,是否可以在根目录和AvatarUpdate组件之间操纵文件数据?还有什么使我无法将文件对象作为属性发送和输出的问题?为什么在AvatarUpdate组件上给我一个空对象?
对于这么多问题我感到抱歉,但我之所以将它们包含在一篇文章中,是因为我认为可能存在某些安全功能,阻止我通过组件发送文件对象。
编辑
这是我的AvatarUpload组件:
<modal v-show="show" heading="Avatar Preview" @close="close">
<div class="flex flex-col">
<h4 class="text-blue-light mb-5">The avatar will be automatically cropped from the center.</h4>
<div class="flex flex-col items-center">
<img class="w-2/3" :src="imgUrl">
</div>
<p>{{imgFile}}</p>
<button class="mt-4 h-10 self-end text-center bg-third-color hover:bg-secondary-color text-white font-bold py-2 px-4 rounded" v-on:click="submitAvatar()">Submit</button>
</div>
<script>
export default {
props: ['show','imgUrl','changeAvatar','imgFile'],
data() {
return {
image: null,
message: null
}
},
methods: {
close: function(){
this.$emit('close');
},
submitAvatar: function(){
console.log(file);
axios({
method: 'POST',
url: '/profile/avatar',
data: {},
}).then(function (response) {
this.message = "Your avatar has been submitted";
}.bind(this))
.catch(function (error) {
console.log(error);
});
}
}
}
</script>
我已经能够从根实例的onFileChange函数中的this.url = URL.createObjectURL(file);
获取blob。我正在尝试使用:img-file="avatarFile"
属性将整个文件对象发送到AvatarUpdate组件。
通过这种方式,我可以通过Laravel控制器中的请求发送数据:
submitAvatar: function(){
//Change here!
var data = new FormData()
var file = this.imgFile;
data.append('avatar', file);
axios({
method: 'POST',
url: '/profile/avatar',
data: data,
}).then(function (response) {
this.message = "Your avatar has been submitted";
}.bind(this))
.catch(function (error) {
console.log(error);
});
}
Laravel UserController
UserController
public function avatar(Request $request)
{
return $request->hasFile('avatar');
}
答案 0 :(得分:1)
在您的代码中,this.avatarFile = file
是一个文件(继承自 Blob )对象,不能直接在image src
中使用(如果打开浏览器检查器,则img:src
的值为[object File]
,显然,该值不是您所期望的。)
您可以使用Javascript MDN: FileReader.readAsDataURL来达到目标。
Javascript MDN: URL.createObjectURL()是另一个解决方案,但是您必须谨慎处理内存管理。选中Javascript MDN: URL.createObjectURL() Usage notes
PS:我建议先将File对象转换为(data-url或object-url),然后再将(data-url或object-url)传递给子组件。直接传递File对象可能会遇到反应性问题。
一个使用 FileReader 的简单演示:
Vue.config.productionTip = false
Vue.component('img-preview', {
template: `<div>
<img :src="imageBlob" alt="test"/>
</div>`,
props: ['imageBlob']
})
new Vue({
el: '#app',
data() {
return {
imageObj: null
}
},
methods:{
onFileChange: function(ev) {
const selectFile = ev.target.files[0]
let reader = new FileReader()
reader.readAsDataURL(selectFile)
reader.addEventListener('load', () => {
this.imageObj = reader.result
//console.log('select image', reader.result)
}, false)
},
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<div>
<label for="avatar">
<img-preview :image-blob="imageObj"></img-preview>
</label>
<input id="avatar" class="hidden" type="file" @change="onFileChange($event)"/>
</div>
</div>
一个简单的演示,它使用 createObjectURL :
Vue.config.productionTip = false
Vue.component('img-preview', {
template: `<div>
<img :src="imageBlob" alt="test"/>
</div>`,
props: ['imageBlob']
})
new Vue({
el: '#app',
data() {
return {
imageObj: null
}
},
methods:{
onFileChange: function(ev) {
const selectFile = ev.target.files[0]
this.imageObj = URL.createObjectURL(selectFile)
},
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<div>
<label for="avatar">
<img-preview :image-blob="imageObj"></img-preview>
</label>
<input id="avatar" class="hidden" type="file" @change="onFileChange($event)"/>
</div>
</div>
下面是一个演示(直接将文件对象传递给子组件):
Vue.config.productionTip = false
Vue.component('img-preview', {
template: `<div>{{imageBlob}}
<img :src="internalImageObj" alt="test"/>
</div>`,
props: ['imageBlob'],
data() {
return {
internalImageObj: ''
}
},
watch: {
imageBlob: function (newVal) {
let reader = new FileReader()
reader.readAsDataURL(newVal)
reader.addEventListener('load', () => {
this.internalImageObj = reader.result
}, false)
}
}
})
new Vue({
el: '#app',
data() {
return {
selectedFile: null
}
},
methods:{
onFileChange: function(ev) {
this.selectedFile = ev.target.files[0]
},
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<div>
<label for="avatar">
<img-preview :image-blob="selectedFile"></img-preview>
</label>
<input id="avatar" class="hidden" type="file" @change="onFileChange($event)"/>
</div>
</div>