如何在Laravel中通过多次浏览上传多张图像?

问题描述 投票:0回答:1

这是我在堆栈溢出中的第一个问题。任何建议将不胜感激。

我正在创建一个网站,它的主要功能是允许用户上传所需数量的图像。它可以来自不同的文件夹,并且用户可以根据需要浏览图像多次。此外,用户可以预览他们选择的图像。如果用户不希望首先选择其中一张照片。用户可以通过单击预览图像附近的按钮将其删除。

在Laravel的View部分,您将看到一个按钮,用于浏览带有文本的多个文件,以告诉用户当前选择了多少张照片。当我使用View Part时,它似乎可以正常工作。但是,结果存储在表格之后。它显示它仅存储了所选照片仅来自上次浏览。

要查看更清晰的图片,这里是示例。

  • 用户选择4张照片,分别是A,B,C和D
  • [预览这4张照片,每张照片旁边都有已删除的按钮
  • 用户不需要D,因此用户可以通过单击预览部分附近的按钮将其删除
  • 左侧的照片是A,B和C
  • 用户要上传更多照片
  • 用户选择另外3张照片,分别是E,F和G
  • 要预览的照片总数为6(A,B,C,E,F,G)
  • A,B和C来自第一次浏览
  • E,F和G来自第二次浏览
  • 用户不想要G,因此用户可以通过单击预览部分附近的按钮将其删除
  • [左照片是A,B,C,E和F
  • 最后,用户单击提交按钮

存储此图像的预期和正确的结果是,将A,B,C,E和F作为从用户选择的照片。但是,事实证明该数据库仅存储E和F,它们是第二次浏览的照片。

我想修复它,以便可以保存正确的照片;在这种情况下,A,B,C,E和F。

我使用JavaScript添加或删除预览图像,并添加复选框以存储所选图像的名称。稍后我将在Controller中使用它来检查它是否是用户要存储的文件。这是代码:

var totalFiles = [];

    function handleFileSelect(evt) {

        // FileList object
        var files = evt.target.files; 

        // Loop through the FileList and render image files as thumbnails.
        for (var i = 0, f; f = files[i]; i++) {

            // Only process image files.
            if (!f.type.match('image.*')) {
                continue;
            }

            var reader = new FileReader();

            // Closure to capture the file information.
            reader.onload = (function(theFile) {
                return function(e) {
                // Render thumbnail.
                var span = document.createElement('span');
                span.innerHTML = ['<input type="checkbox" name="selected[]" value="' , theFile.name, '" style="display: none;" checked /><img width=50% height="auto" class="thumb p-3" src="', e.target.result,
                                    '" title="', theFile.name, '"/>', "<button onclick='deleteImage()'>" + "<i class='fas fa-trash-alt' aria-hidden='true'></i>" + " ลบรูปภาพ</button><br/>"].join('');

                document.getElementById('preview_photo').insertBefore(span, null);
                };
            })(f);

            totalFiles.push(f);

            // Read in the image file as a data URL.
            reader.readAsDataURL(f);
        }

        if(Array.from(totalFiles).length > 0){
            document.getElementById('count_selected_photo').innerHTML = "Image Selected:  " + Array.from(totalFiles).length + " Photo(s)";
        }
        else{
            document.getElementById('count_selected_photo').innerHTML = "No Image Selected";
        }
    }


    function deleteImage() { 
        var index = Array.from(document.getElementById('preview_photo').children).indexOf(event.target.parentNode)
        document.querySelector("#preview_photo").removeChild( document.querySelectorAll('#preview_photo span')[index]);

        totalFiles.splice(index, 1); 

        if(Array.from(totalFiles).length > 0){
            document.getElementById('count_selected_photo').innerHTML = "Image Selected:  " + Array.from(totalFiles).length + " Photo(s)";
        }
        else{
            document.getElementById('count_selected_photo').innerHTML = "No Image Selected";
        }
    }

    document.getElementById('before_photo').addEventListener('change', handleFileSelect, false);

在Controller中,我使用request->file('before_photo')删除之前选择的文件,并使用request->input('selected')获取选择的照片的名称,并将这两个文件进行比较。如果request->file('before_photo')中的文件名与request->input('selected')中的文件名相同,它将存储该照片。如果不是,则意味着用户在单击“提交”之前将其删除,它将不会存储。

这是我在Controller中的代码的一部分:

// Store Photo If Existed
                    if (request()->hasFile('before_photo')) {
                        // File Chosen at First Time
                        $before_photos = $request->file('before_photo');

                        // File Chosen After Delete Image
                        $selected_photos = $request->input('selected');

                        // Collect Path of Each Photo
                        $paths  = [];

                        // Collect File Name of Each Photo
                        $filenames = [];

                        foreach($selected_photos as $selected_photo){
                            foreach ($before_photos as $before_photo) {
                                // If File Name of Chosen Photo at First Time = File Name of Photo After Deleting
                                if($before_photo->getClientOriginalName() == $selected_photo){

                                    // Transform The File Name to be 'before-photo-TIME-name-originalFileName'
                                    $filename = "before-photo-" . time() . "-name-" . $before_photo->getClientOriginalName();
                                    $paths[] = $before_photo->storeAs('', $filename, 'irepair_photo');
                                    array_push($filenames, $filename);
                                }
                            }
                        }

                        // Transform List of Photo Name into Array
                        $repair_ticket->before_photo = $filenames;
                    }

如果对此有任何建议或意见,请在下面留言。谢谢您的关注。

javascript laravel input image-uploading
1个回答
0
投票

这可能不是您要查找的内容,但应该引导您朝着正确的方向前进。我看到您使用的是原始JavaScript,这可能会使事情变得更棘手。但是由于您还使用了Laravel,并且Laravel附带了Vue支持(至少以前是这样),所以我将向您展示我使用Vue的实现。如果您知道JavaScript,那么它就会变得不难理解,因为Vue非常容易。

多文件上传的技巧是使用另一个数组存储文件,因为文件输入返回的本机FileList对象是不可变的。查看下面的代码片段(我试图添加注释以解释我在做什么。)

您可以在此pen中看到一个有效的示例。

<!-- @change is the Vue equivalent of onchange -->
<input @change="handleFileSelect" type="file" multiple>
<ul>
  <li v-for="(photo, index) in photos" :key="`thumb-${index}`">
    <img :src="photo.preview" :alt="photo.file.name">
    <button @click="removePhoto(index)" type="button">Remove Photo</button>
  </li>
</ul>

在HTML部分,我们要做的就是聆听对文件输入的更改,并设置一个可以显示所选图像缩略图的位置。 Vue通过使用v-for指令在数组上进行迭代使此操作非常容易。迭代中的当前元素可用于li及其子元素,我们可以使用它来填充属性,例如imgsrc

export default {
  data() {
    return {
      photos: []
    }
  },
  methods: {
    handleFileSelect(e) {
      Array.from(e.target.files).forEach(file => {
        // perform check here such as making sure its an image

        const reader = new FileReader()

        reader.onload = () => {
          // push the preview result and also the file to photos array
          this.photos.push({
            preview: reader.result,
            file
          })
        }

        reader.readAsDataURL(file)
      })
    },
    removePhoto(index) {
      this.photos.splice(index, 1)
    },
    upload() {
      const fd = new FormData()

      this.photos.forEach((photo, index) => {
        // we only want to append the actual file, excluding the preview
        // so we only append the `file` attribute
        fd.append(`photo-${index}`, photo.file)
      })

      // send the data
      // for example, using axios
      const uploadEndpoint = ''
      axios.post(uploadEndpoint, fd, {
        headers: {
          'Content-Type': 'multipart/form-data' // important
        }
      })
    }
  }
}

JavaScript部分非常简单。我们在这里所做的是设置photos“本地状态”来存储我们的照片。我们使用此变量来迭代并在HTML中显示缩略图。当用户选择图像时,我们将使用FileReader对象获取文件并为其生成缩略图,然后将其与实际文件一起推送至photos变量。

{
  preview: 'data:image/....', // the file preview generated by the FileReader
  file: File object // the actual file the user selected
}

[当用户想要取消选择图像时,我们只是从splice数组中选择photos,因此photos数组将始终包含用户要上传的照片。上载时,如果我们使用AJAX发送请求,则可以创建一个FormData对象,并通过遍历photos变量将每张照片附加到该对象上。在Laravel后端,可以使用request()->file()访问这些照片。在发送请求之前,您必须设置适当的标题,主要是Content-Type: multipart/form-data

我希望这给了您一些有关如何解决此问题的一般思路。如果您有任何问题,请随时将其留在评论部分,我将尽力解释更多。

© www.soinside.com 2019 - 2024. All rights reserved.