使文件 Uploader/Preview 处理多个文件
Make File Uploader/Preview Handle Multiple Files
我构建了一个文件上传器(在后端 php 上 运行s)在上传之前预览图像文件。
我遇到的问题是无法处理多个文件。
这是根据我看过的教程改编的,问题的症结在updateThumbnail
函数。当为多个文件上传调用此函数时,我想我需要将第二个参数从 fileUploader.files[0]
更改为 fileUploader.files
,但我正在努力处理实际函数本身。
我显然需要在 updateThumbnail
函数中 运行 一个 foreach 循环(或类似循环),但我无法让它发挥作用。
注意: CodePen 似乎不允许拖放功能,但有一个输入文件元素也分配给隐藏在HTML 与 display:none。这使用点击事件侦听器和 fileUploader.click()
因此当您单击拖放区时可以调出文件选择器 window.
代码笔:https://codepen.io/pauljohnknight/pen/JjNNyzO
// hidden on the form, but has drag & drop files assigned to it
var fileUploader = document.getElementById("standard-upload-files");
var dropZone = document.getElementById("drop-zone");
var showSelectedImages = document.getElementById("show-selected-images");
dropZone.addEventListener("click", (e) => {
//assigns the dropzone to the hidden input element so when you click 'select files' it brings up a file picker window
fileUploader.click();
});
fileUploader.addEventListener("change", (e) => {
if (fileUploader.files.length) {
// this function is further down but declared here and shows a thumbnail of the image
updateThumbnail(dropZone, fileUploader.files[0]);
}
});
dropZone.addEventListener('dragover', e => {
e.preventDefault()
})
dropZone.addEventListener('dragend', e => {
e.preventDefault()
})
// When the files are dropped in the 'drop-zone'
dropZone.addEventListener("drop", (e) => {
e.preventDefault();
// assign dropped files to the hidden input element
if (e.dataTransfer.files.length) {
fileUploader.files = e.dataTransfer.files;
}
// function is declared here but written further down
updateThumbnail(dropZone, e.dataTransfer.files[0]);
});
// updateThumbnail function that needs to be able to handle multiple files
function updateThumbnail(dropZone, file) {
var thumbnailElement = document.querySelector(".drop-zone__thumb");
if (!thumbnailElement) {
thumbnailElement = document.createElement("img");
thumbnailElement.classList.add("drop-zone__thumb");
// append to showSelectedImages div
showSelectedImages.appendChild(thumbnailElement);
}
if (file.type.startsWith("image/")) {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
thumbnailElement.src = reader.result;
};
} else {
thumbnailElement.src = null;
}
} // end of 'updateThumbnail' function
body {
margin: 0;
display: flex;
justify-content: center;
width: 100%;
}
form {
width: 30%;
}
#drop-zone {
border: 1px dashed;
width: 100%;
padding: 1rem;
margin-bottom: 1rem;
}
.select-files {
text-decoration: underline;
cursor: pointer;
}
/* image that is preview prior to form submit*/
.drop-zone__thumb {
width: 200px;
height: auto;
display: block;
}
#submit-images {
margin-top: 1rem;
}
<form id="upload-images-form" enctype="multipart/form-data" method="post">
<h1>Upload Your Images</h1>
<div id="drop-zone" class="drop-zone flex">
<p class="td text-center">DRAG AND DROP IMAGES HERE</p>
<p>Or</p>
<p class="select-files">Select Files</p>
</div>
<div id="show-selected-images"></div>
<div class="inner-input-wrapper">
<div class="upload-label-wrapper">
<input id="standard-upload-files" style="display:none" type="file" name="standard-upload-files[]" multiple>
</div>
<input type="submit" name="submit-images" id="submit-images" value="SUBMIT IMAGES">
</div>
</form>
我从你的 Codepen
中快速 Codesandbox example
是的,您只需要遍历文件并为每个文件添加预览。您可以使用 for
循环或只使用 Array.from
然后 .forEach
(因为 FileList
不是真正的数组,您需要先将其转换为数组才能使用数组内置方法)
Array.from(fileUploader.files).forEach((file) => {
updateThumbnail(dropZone, file);
});
至于预览和 updateThumbnail
功能 - 这完全取决于您要如何使用它。如果您希望用户能够在第一个 selection 之后添加更多文件,那么您可以只添加新的预览。如果用户 select 新的要清除旧的,则需要删除旧的预览。或者您可以为每个预览添加“删除”按钮,以便用户可以在添加后删除其中一个。
以下是您只想附加新预览时的变体:
function updateThumbnail(dropZone, file) {
if (file.type.startsWith('image/')) {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
var thumbnailElement = document.createElement('img');
thumbnailElement.classList.add('drop-zone__thumb');
thumbnailElement.src = reader.result;
showSelectedImages.appendChild(thumbnailElement);
};
}
}
对于 drop 你基本上做同样的事情:
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
// .. do whatever you want or need here
Array.from(e.dataTransfer.files).forEach((file) => {
updateThumbnail(dropZone, file);
});
});
正如你所看到的处理 drop
和 select
的函数非常相似,你甚至可以创建单独的函数来接受 fileList
然后用它做一些事情,所以不会需要为这两种情况复制代码。
对@Danila 的版本做了一些小改动
最显着的区别是使用 es6 和使用 URL.createObjectURL 更快加载图像的方法。当原型本身有对象 url + 新的基于 promise 的读取方法时,文件 reader 现在几乎是一个古老的东西。使用bae64是浪费时间decoding/encodingto/frombase64
https://codesandbox.io/s/httpsWhosebugcomquestions68416563-forked-o5spy?file=/src/index.js
import "./styles.css";
// Query all needed elements in one go
const [dropZone, showSelectedImages, fileUploader] = document.querySelectorAll(
"#standard-upload-files, #drop-zone, #show-selected-images"
);
dropZone.addEventListener("click", (evt) => {
// assigns the dropzone to the hidden input element so when you click 'select files' it brings up a file picker window
fileUploader.click();
});
// Prevent browser default when draging over
dropZone.addEventListener("dragover", (evt) => {
evt.preventDefault();
});
fileUploader.addEventListener("change", (evt) => {
// Clear the already selected images
showSelectedImages.innerHTML = "";
// this function is further down but declared here and shows a thumbnail of the image
[...fileUploader.files].forEach(updateThumbnail);
});
dropZone.addEventListener("drop", (evt) => {
evt.preventDefault();
// Clear the already selected images
showSelectedImages.innerHTML = "";
// assign dropped files to the hidden input element
if (evt.dataTransfer.files.length) {
fileUploader.files = evt.dataTransfer.files;
}
// function is declared here but written further down
[...evt.dataTransfer.files].forEach(updateThumbnail);
});
// updateThumbnail function that needs to be able to handle multiple files
function updateThumbnail(file) {
if (file.type.startsWith("image/")) {
const thumbnailElement = new Image();
thumbnailElement.classList.add("drop-zone__thumb");
thumbnailElement.src = URL.createObjectURL(file);
showSelectedImages.append(thumbnailElement);
}
} // end of 'updateThumbnail' function
我构建了一个文件上传器(在后端 php 上 运行s)在上传之前预览图像文件。
我遇到的问题是无法处理多个文件。
这是根据我看过的教程改编的,问题的症结在updateThumbnail
函数。当为多个文件上传调用此函数时,我想我需要将第二个参数从 fileUploader.files[0]
更改为 fileUploader.files
,但我正在努力处理实际函数本身。
我显然需要在 updateThumbnail
函数中 运行 一个 foreach 循环(或类似循环),但我无法让它发挥作用。
注意: CodePen 似乎不允许拖放功能,但有一个输入文件元素也分配给隐藏在HTML 与 display:none。这使用点击事件侦听器和 fileUploader.click()
因此当您单击拖放区时可以调出文件选择器 window.
代码笔:https://codepen.io/pauljohnknight/pen/JjNNyzO
// hidden on the form, but has drag & drop files assigned to it
var fileUploader = document.getElementById("standard-upload-files");
var dropZone = document.getElementById("drop-zone");
var showSelectedImages = document.getElementById("show-selected-images");
dropZone.addEventListener("click", (e) => {
//assigns the dropzone to the hidden input element so when you click 'select files' it brings up a file picker window
fileUploader.click();
});
fileUploader.addEventListener("change", (e) => {
if (fileUploader.files.length) {
// this function is further down but declared here and shows a thumbnail of the image
updateThumbnail(dropZone, fileUploader.files[0]);
}
});
dropZone.addEventListener('dragover', e => {
e.preventDefault()
})
dropZone.addEventListener('dragend', e => {
e.preventDefault()
})
// When the files are dropped in the 'drop-zone'
dropZone.addEventListener("drop", (e) => {
e.preventDefault();
// assign dropped files to the hidden input element
if (e.dataTransfer.files.length) {
fileUploader.files = e.dataTransfer.files;
}
// function is declared here but written further down
updateThumbnail(dropZone, e.dataTransfer.files[0]);
});
// updateThumbnail function that needs to be able to handle multiple files
function updateThumbnail(dropZone, file) {
var thumbnailElement = document.querySelector(".drop-zone__thumb");
if (!thumbnailElement) {
thumbnailElement = document.createElement("img");
thumbnailElement.classList.add("drop-zone__thumb");
// append to showSelectedImages div
showSelectedImages.appendChild(thumbnailElement);
}
if (file.type.startsWith("image/")) {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
thumbnailElement.src = reader.result;
};
} else {
thumbnailElement.src = null;
}
} // end of 'updateThumbnail' function
body {
margin: 0;
display: flex;
justify-content: center;
width: 100%;
}
form {
width: 30%;
}
#drop-zone {
border: 1px dashed;
width: 100%;
padding: 1rem;
margin-bottom: 1rem;
}
.select-files {
text-decoration: underline;
cursor: pointer;
}
/* image that is preview prior to form submit*/
.drop-zone__thumb {
width: 200px;
height: auto;
display: block;
}
#submit-images {
margin-top: 1rem;
}
<form id="upload-images-form" enctype="multipart/form-data" method="post">
<h1>Upload Your Images</h1>
<div id="drop-zone" class="drop-zone flex">
<p class="td text-center">DRAG AND DROP IMAGES HERE</p>
<p>Or</p>
<p class="select-files">Select Files</p>
</div>
<div id="show-selected-images"></div>
<div class="inner-input-wrapper">
<div class="upload-label-wrapper">
<input id="standard-upload-files" style="display:none" type="file" name="standard-upload-files[]" multiple>
</div>
<input type="submit" name="submit-images" id="submit-images" value="SUBMIT IMAGES">
</div>
</form>
我从你的 Codepen
中快速 Codesandbox example是的,您只需要遍历文件并为每个文件添加预览。您可以使用 for
循环或只使用 Array.from
然后 .forEach
(因为 FileList
不是真正的数组,您需要先将其转换为数组才能使用数组内置方法)
Array.from(fileUploader.files).forEach((file) => {
updateThumbnail(dropZone, file);
});
至于预览和 updateThumbnail
功能 - 这完全取决于您要如何使用它。如果您希望用户能够在第一个 selection 之后添加更多文件,那么您可以只添加新的预览。如果用户 select 新的要清除旧的,则需要删除旧的预览。或者您可以为每个预览添加“删除”按钮,以便用户可以在添加后删除其中一个。
以下是您只想附加新预览时的变体:
function updateThumbnail(dropZone, file) {
if (file.type.startsWith('image/')) {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
var thumbnailElement = document.createElement('img');
thumbnailElement.classList.add('drop-zone__thumb');
thumbnailElement.src = reader.result;
showSelectedImages.appendChild(thumbnailElement);
};
}
}
对于 drop 你基本上做同样的事情:
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
// .. do whatever you want or need here
Array.from(e.dataTransfer.files).forEach((file) => {
updateThumbnail(dropZone, file);
});
});
正如你所看到的处理 drop
和 select
的函数非常相似,你甚至可以创建单独的函数来接受 fileList
然后用它做一些事情,所以不会需要为这两种情况复制代码。
对@Danila 的版本做了一些小改动
最显着的区别是使用 es6 和使用 URL.createObjectURL 更快加载图像的方法。当原型本身有对象 url + 新的基于 promise 的读取方法时,文件 reader 现在几乎是一个古老的东西。使用bae64是浪费时间decoding/encodingto/frombase64
https://codesandbox.io/s/httpsWhosebugcomquestions68416563-forked-o5spy?file=/src/index.js
import "./styles.css";
// Query all needed elements in one go
const [dropZone, showSelectedImages, fileUploader] = document.querySelectorAll(
"#standard-upload-files, #drop-zone, #show-selected-images"
);
dropZone.addEventListener("click", (evt) => {
// assigns the dropzone to the hidden input element so when you click 'select files' it brings up a file picker window
fileUploader.click();
});
// Prevent browser default when draging over
dropZone.addEventListener("dragover", (evt) => {
evt.preventDefault();
});
fileUploader.addEventListener("change", (evt) => {
// Clear the already selected images
showSelectedImages.innerHTML = "";
// this function is further down but declared here and shows a thumbnail of the image
[...fileUploader.files].forEach(updateThumbnail);
});
dropZone.addEventListener("drop", (evt) => {
evt.preventDefault();
// Clear the already selected images
showSelectedImages.innerHTML = "";
// assign dropped files to the hidden input element
if (evt.dataTransfer.files.length) {
fileUploader.files = evt.dataTransfer.files;
}
// function is declared here but written further down
[...evt.dataTransfer.files].forEach(updateThumbnail);
});
// updateThumbnail function that needs to be able to handle multiple files
function updateThumbnail(file) {
if (file.type.startsWith("image/")) {
const thumbnailElement = new Image();
thumbnailElement.classList.add("drop-zone__thumb");
thumbnailElement.src = URL.createObjectURL(file);
showSelectedImages.append(thumbnailElement);
}
} // end of 'updateThumbnail' function