Попытка передать объект File как свойство компоненту и получение пустого объекта. Мне также интересно, это безопасно? - PullRequest
0 голосов
/ 30 августа 2018

У меня есть модал предварительного просмотра аватара:

<avatar-update :img-file="avatarFile" :show="avatarModalShow" :img-url="url" @close="avatarModalShow = !avatarModalShow" :change-avatar="updateCrop" @destroyUrl="imgUrl = null"> </avatar-update>

При отправке аватара я использую свой root для отправки нескольких свойств компоненту 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>

Root

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;
},

Когда я console.log файл const в функции onFileChange, я получаю объект файла. Однако когда я пытаюсь вывести свойство {{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>

Я уже могу получить BLOB-объект из this.url = URL.createObjectURL(file); в функции onFileChange на корневом экземпляре. То, что я пытаюсь сделать, это отправить весь объект файла в компонент AvatarUpdate с помощью :img-file="avatarFile" prop.

Таким образом, я могу отправить данные способом, к которому можно получить доступ по запросу в контроллере 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');
}

1 Ответ

0 голосов
/ 30 августа 2018

В ваших кодах this.avatarFile = 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>

Ниже одна демонстрация (непосредственно передать File Object дочернему компоненту):

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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...