从FilePicker向使用DataTransfer创建的现有数组添加更多文件选择-JavaScript



我有一个文件上传器,当从图像预览中删除图像时,该图像将从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>

这使用一个名为submitDataaddDataTransfer对象—在change事件中选择的文件到attachFiles,并在click事件中使用remove将其预览到<img>

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

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>

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

大多数情况下,它会在动态创建的Image()上使用addEventListener在附加到DOM之前附加click侦听器,而不是在随后附加并查询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);
}
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>

最新更新