代码之家  ›  专栏  ›  技术社区  ›  pjk_ok

将FilePicker中的第二个文件选择添加到使用DataTransfer创建的现有数组-JavaScript

  •  0
  • pjk_ok  · 技术社区  · 3 年前

    我有一个文件上传器,当从图像预览中删除图像时,该图像将从 FileList 。这是通过创建一个新数组并从新数组中删除目标(已删除)元素,然后使用 dataTranser() 将其设置为“file”输入元素的FileList。所有这些都可以正常工作。

    问题

    我遇到的问题是,如果在提交之前通过filepicker输入元素添加了更多的文件(无论是否删除了任何文件),即使图像预览在显示的图像数量和特定图像方面是正确的,上传/提交的文件始终是用最新选择添加的文件。如果只选择了一组文件,这当然是可以的,但如果在提交之前添加了额外的图像/文件,则第二组将覆盖第一组(尽管图像在图像预览中仍然正确显示)。

    要查看问题,如果您通过代码示例中的“浏览”按钮选择要上传的文件,然后第二次单击“浏览”键添加更多图像,您就会明白我的意思。

    问题

    我如何获得它,以便在添加第二个文件选择时,这些文件会添加到现有阵列中,而不会覆盖它?

    注1: 为了使代码更简单,我设置了它,以便在单击特定图像预览时删除图像,而不是通过“删除图像”按钮。

    注2: 用于处理表单的后端代码是PHP,但我没有包括它,因为它不是问题的一部分。

    代码笔: https://codepen.io/thechewy/pen/wvjOdEq

    let attachFiles = document.getElementById("attach-files");
    let previewWrapper = document.getElementById("show-selected-images");
    let form = document.getElementById("upload-images-form");
    
    attachFiles.addEventListener("change", (e) => {
      [...e.target.files].forEach(showFiles);
    });
    
    function showFiles(file) {
      let previewImage = new Image();
    
      previewImage.dataset.name = file.name;
      previewImage.classList.add("img");
      previewImage.src = URL.createObjectURL(file);
    
      previewWrapper.append(previewImage); // append preview image
    
      // -- remove the image preview visually and change FileList
      document.querySelectorAll(".img").forEach((i) => {
        i.addEventListener("click", (e) => {
          const transfer = new DataTransfer();
          const name = e.target.dataset.name;
    
          for (const file of attachFiles.files) {
            if (file.name !== name) {
              transfer.items.add(file);
            }
          }
    
          attachFiles.files = transfer.files;
    
          //remove image preview element when image is clicked
          e.target.remove();
        });
      });
    }
    form {
      padding: 1rem 2rem;
      width: 50%;
      border: 1px solid;
    }
    
    input,
    button {
      display: block;
      margin: 2rem 0;
    }
    
    .img {
      width: 200px;
      height: 200px;
      object-fit: cover;
      margin: 0 1rem;
    }
    
    img:hover {
      cursor: pointer;
    }
    <form enctype="multipart/form-data" method="post" id="upload-images-form">
      <input id="attach-files" type="file" name="attach-files[]" multiple>
      <button name="submit" id="submit">SUBMIT</button>
      <div id="show-selected-images"></div>
    </form>
    0 回复  |  直到 3 年前
        1
  •  1
  •   morganney    3 年前

    这使用一个 DataTransfer 对象被调用 submitData add 在期间选择的文件 change 事件到 attachFiles remove 他们在上 click 事件到 <img> 预览

    如果您想用做一些不同的事情,请告诉我 FileList 在…上 submit ,比如可能使用POST fetch

    let attachFiles = document.getElementById("attach-files");
    let previewWrapper = document.getElementById("show-selected-images");
    let form = document.getElementById("upload-images-form");
    let submitData = new DataTransfer();
    
    attachFiles.addEventListener("change", (e) => {
      const currentSubmitData = Array.from(submitData.files);
    
      previewWrapper.replaceChildren();
    
      [...e.target.files].forEach(file => {
        if (currentSubmitData.every(currFile => currFile.name !== file.name)) {
          submitData.items.add(file);
        }
      });
      [...submitData.files].forEach(showFiles);
      attachFiles.files = submitData.files;
    });
    
    function showFiles(file) {
      let previewImage = new Image();
    
      previewImage.dataset.name = file.name;
      previewImage.classList.add("img");
      previewImage.src = URL.createObjectURL(file);
    
      previewWrapper.append(previewImage); // append preview image
    
      // -- remove the image preview visually and change FileList
      document.querySelectorAll(".img").forEach((i) => {
        i.addEventListener("click", (e) => {
          const name = e.target.dataset.name;
    
          [...submitData.files].forEach((file, idx) => {
            if (file.name === name) {
              submitData.items.remove(idx);
            }
          });
          attachFiles.files = submitData.files;
    
          //remove image preview element when image is clicked
          e.target.remove();
        });
      });
    }
    form {
      padding: 1rem 2rem;
      width: 50%;
      border: 1px solid;
    }
    
    input,
    button {
      display: block;
      margin: 2rem 0;
    }
    
    .img {
      width: 200px;
      height: 200px;
      object-fit: cover;
      margin: 0 1rem;
    }
    
    img:hover {
      cursor: pointer;
    }
    <form enctype="multipart/form-data" method="post" id="upload-images-form">
      <input id="attach-files" type="file" name="attach-files[]" multiple>
      <button name="submit" id="submit">SUBMIT</button>
      <div id="show-selected-images"></div>
    </form>

    这里有一种替代方法,可以清理代码的其他一些区域。有内联注释解释该方法。

    它主要利用 addEventListener 在您动态创建的 Image() 连接 点击 侦听器,然后再附加到DOM,而不是附加然后查询DOM以随后附加侦听器。也使用 currentTarget 而不是 target 以确保事件的任何冒泡阶段都不会触发意外DOM节点的侦听器。

    let attachFiles = document.getElementById("attach-files");
    let previewWrapper = document.getElementById("show-selected-images");
    let form = document.getElementById("upload-images-form");
    let submitData = new DataTransfer();
    
    attachFiles.addEventListener("change", (e) => {
      const currentSubmitData = Array.from(submitData.files);
    
      // For each addded file, add it to submitData if not already present
      [...e.target.files].forEach(file => {
        if (currentSubmitData.every(currFile => currFile.name !== file.name)) {
          submitData.items.add(file);
        }
      });
    
      // Sync attachFiles FileList with submitData FileList
      attachFiles.files = submitData.files;
    
      // Clear the previewWrapper before generating new previews
      previewWrapper.replaceChildren();
      
      // Generate a preview <img> for each selected file
      [...submitData.files].forEach(showFiles);
    });
    
    function showFiles(file) {
      let previewImage = new Image();
    
      // Set relevant <img> attributes
      previewImage.dataset.name = file.name;
      previewImage.classList.add("img");
      previewImage.src = URL.createObjectURL(file);
      
      // Add click event listener to <img> preview
      previewImage.addEventListener('click', e => {
        const target = e.currentTarget;
        const name = target.dataset.name;
    
        // Remove the clicked file from the submitData
        [...submitData.files].forEach((file, idx) => {
          if (file.name === name) {
            submitData.items.remove(idx);
          }
        });
    
        // Reset the attachFiles FileList
        attachFiles.files = submitData.files;
    
        // Remove the <img> node from the DOM
        target.remove();
      });
     
      // Append <img> preview node to DOM
      previewWrapper.append(previewImage);
    }
    形式{
    填充:1rem2rem;
    宽度:50%;
    边框:1px实心;
    }
    
    输入
    按钮{
    显示:块;
    边距:2rem 0;
    }
    
    .img{
    宽度:200px;
    高度:200px;
    对象适合:封面;
    边距:0 1rem;
    }
    
    img:悬停{
    游标:指针;
    }
    <form enctype=“multipart/form data”method=“post”id=“上传图像表单”>
    <input id=“attach files”type=“file”name=“attach files[]”多个>
    <button name=“submit”id=“submit”>提交</按钮>
    <div id=“显示所选图像”></div>
    </表单>