首页 > 解决方案 > 试图将 File 对象作为属性传递给组件并获取一个空对象。我也想知道这是否安全?

问题描述

我有一个通用的头像预览模式:

<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 时,我得到了文件对象。但是,当我尝试{{imgFile}}在 AvatarUpdate 组件中输出属性时,我得到一个空对象。

我想知道这是否安全以及是否可以在根和 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>

我已经能够从this.url = URL.createObjectURL(file);根实例的 onFileChange 函数中获取 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 用户控制器

用户控制器

public function avatar(Request $request)
{
    return $request->hasFile('avatar');
}

标签: javascriptlaravelsecurityvue.js

解决方案


在您的代码中,this.avatarFile = file是一个File(继承自Blob)对象,不能image src直接使用(如果打开浏览器检查器,值为img:src[object File]显然该值不是您所期望的)。

您可以使用Javascript MDN:FileReader.readAsDataURL来达到目标​​。

Javascript MDN:URL.createObjectURL()是另一种解决方案,但您必须小心处理内存管理。检查Javascript MDN: URL.createObjectURL() 使用说明

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>


推荐阅读