作者:私语茶馆
非常多的云应用中需要上传文本,包括图片,文件等等,这些批量文件上传,往往涉及到进度条显示,多文件上传等,这里分享一个非常好的案例,来自BootStrapfriendly.com,方便大家开发产品时使用。
已验证的场景:PHP+JavaScript; Servlet+JavaScript; 使用的环境:Tomcat
1.异步上传文件使用的基本概念
1.1.AJAX
进度条本质是用脚本控制ProgressBar的显示,其中会用到AJAX,AJAX有如下特征:
- AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
- AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
- AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。
- AJAX 不需要任何浏览器插件,但需要用户允许 JavaScript 在浏览器上执行。
- XMLHttpRequest 只是实现 Ajax 的一种方式。
1.2. Form
<form id="upload_form" enctype="multipart/form-data" method="post">
enctype=“multipart/form-data”: 指form中包含二进制的文件形式
1.3.XMLHttpRequest
XMLHttpRequest(XHR)对象用于与服务器后端服务做请求交互,可以在不刷新页面的情况下请求特定的URL,获取数据。允许网元在不影响用户操作的情况下,更新页面的局部内容,是AJAX的一种关键使用绩效。
XMLHttpRequest可以支持多种协议,包括FTP, file等。
如果需要处理消息事件,可以使用EventSource,如果是全双工的可以是WebSocket。
参考:XMLHttpRequest - Web API | MDN (mozilla.org)
1.2.项目结构优化-独立的Scripts脚本
从项目结构上讲,Script最好是独立文件,可以使用<script src="myscripts.js"></script>来引入,这样可以保持HTML简洁。
如下图所示:
创建Scripts脚本
HTML引用该脚本
Script脚本
注意事项:要独立一个InitialLoad,并通过window.οnlοad=initialLoad来指定,如果脚本不起作用,可以检查一下windows.onload有没有设置。
2.异步批量上传文件及进度条案例
这里前台部分直接使用Bootstrapfriedly.com的案例,后台用的是Apach的fileupload组件实现多文件上传,URL部分不同。
2.1. 前台界面
<div class="ath_container tile-container ">
<div id="uploadStatus"></div>
<h2 style="margin-bottom:10px">AJAX File Upload with Progress Bar using JavaScript</h2>
<input type="file" id="fileUpload" multiple placeholder="choose file or browse" /> <!-- Add 'multiple' attribute for multiple file selection -->
<br>
<br>
<button onclick="uploadFiles()">Upload</button> <!-- Change function name -->
<div>
<table id="progressBarsContainer">
<!-- Table rows will be dynamically added here -->
</table>
</div> <!-- Container for progress bars -->
<br>
</div>
说明点:
(1)这里没有直接使用Form,用button的Click来处理,后面在Script中使用AJAX的XMLHttpRequest提交表单。
(2)table id=progressBarsContainer做为占位符号,后续ajax发送请求后,用于增量显示图片文件状态用。
2.2. CSS风格文件
* {
margin: 0px;
padding: 0px;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, Roboto, Segoe UI,
Helvetica Neue, Helvetica, Arial, sans-serif;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
box-sizing: border-box;
color:
#2f2f2f;
line-height: 1.5;
}
.ath_container {
width: 740px;
margin: 20px auto;
padding: 0px 20px 0px 20px;
}
.ath_container {
width: 820px;
border:
#d7d7d7 1px solid;
border-radius: 5px;
padding: 10px 20px 10px 20px;
box-shadow: 0 0 5px
rgba(0, 0, 0, .3);
/* border-radius: 5px; */
}
#uploadStatus {
color:
#00e200;
}
.ath_container a {
text-decoration: none;
color:
#2f20d1;
}
.ath_container a:hover {
text-decoration: underline;
}
.ath_container img {
height: auto;
max-width: 100%;
vertical-align: middle;
}
.ath_container .label {
color:
#565656;
margin-bottom: 2px;
}
.ath_container .message {
padding: 6px 20px;
font-size: 1em;
color:
rgb(40, 40, 40);
box-sizing: border-box;
margin: 0px;
border-radius: 3px;
width: 100%;
overflow: auto;
}
.ath_container .error {
padding: 6px 20px;
border-radius: 3px;
background-color:
#ffe7e7;
border: 1px solid
#e46b66;
color:
#dc0d24;
}
.ath_container .success {
background-color:
#48e0a4;
border:
#40cc94 1px solid;
border-radius: 3px;
color:
#105b3d;
}
.ath_container .validation-message {
color:
#e20900;
}
.ath_container .font-bold {
font-weight: bold;
}
.ath_container .display-none {
display: none;
}
.ath_container .inline-block {
display: inline-block;
}
.ath_container .float-right {
float: right;
}
.ath_container .float-left {
float: left;
}
.ath_container .text-center {
text-align: center;
}
.ath_container .text-left {
text-align: left;
}
.ath_container .text-right {
text-align: right;
}
.ath_container .full-width {
width: 100%;
}
.ath_container .cursor-pointer {
cursor: pointer;
}
.ath_container .mr-20 {
margin-right: 20px;
}
.ath_container .m-20 {
margin: 20px;
}
.ath_container table {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
border: 1px solid
#ddd;
margin-top: 20px;
}
.ath_container table th,
.ath_container table td {
text-align: left;
padding: 5px;
border: 1px solid
#ededed;
width: 50%;
}
tr:nth-child(even) {
background-color:
#f2f2f2
}
.ath_container .progress {
margin: 20px 0 0 0;
width: 300px;
border: 1px solid
#ddd;
padding: 5px;
border-radius: 5px;
}
.ath_container .progress-bar {
width: 0%;
height: 24px;
background-color:
#4CAF50;
margin-top: 15px;
border-radius: 12px;
text-align: center;
color:
#fff;
}
@media all and (max-width: 780px) {
.ath_container {
width: auto;
}
}
.ath_container input,
.ath_container textarea,
.ath_container select {
box-sizing: border-box;
width: 200px;
height: initial;
padding: 8px 5px;
border: 1px solid
#9a9a9a;
border-radius: 4px;
}
.ath_container input[type="checkbox"] {
width: auto;
vertical-align: text-bottom;
}
.ath_container textarea {
width: 300px;
}
.ath_container select {
display: initial;
height: 30px;
padding: 2px 5px;
}
.ath_container button,
.ath_container input[type=submit],
.ath_container input[type=button] {
padding: 8px 30px;
font-size: 1em;
cursor: pointer;
border-radius: 25px;
color:
#ffffff;
background-color:
#6213d3;
border-color:
#9554f1
#9172bd
#4907a9;
}
.ath_container input[type=submit]:hover {
background-color:
#f7c027;
}
::placeholder {
color:
#bdbfc4;
}
.ath_container label {
display: block;
color:
#565656;
}
@media all and (max-width: 400px) {
.ath_container {
padding: 0px 20px;
}
.ath_container {
width: auto;
}
.ath_container input,
.ath_container textarea,
.ath_container select {
width: 100%;
}
}
2.3. JavaScript脚本
function uploadFiles() {
var fileInput = document.getElementById('fileUpload');
var files = fileInput.files;
//(1)校验图片文件,并上传
for (var i = 0; i < files.length; i++) {
var allowedExtensions = ['.jpg', '.jpeg', '.png', '.pdf', '.svg', '.zip', '.docx', '.xlsx'];
var fileExtension = files[i].name.substring(files[i].name.lastIndexOf('.')).toLowerCase();
if (allowedExtensions.includes(fileExtension)) {
//(1.1)一次上传一个文件,并显示文件名和进度
uploadFile(files[i]);
} else {
alert('Invalid file type: ' + fileExtension);
}
}
}
function uploadFile(file) {
var formData = new FormData();
formData.append('file', file);
var progressBarContainer = document.createElement('div'); // Container for progress bar and file name
progressBarContainer.className = 'progress-container';
var fileName = document.createElement('div'); // Display file name
fileName.className = 'file-name';
fileName.textContent = file.name;
//progressBarContainer.appendChild(fileName);
var progressBar = document.createElement('div'); // Create a new progress bar element
progressBar.className = 'progress-bar';
progressBar.id = 'progressBar_' + file.name;
progressBarContainer.appendChild(progressBar);
var progressBarsContainer = document.getElementById('progressBarsContainer');
var newRow = document.createElement('tr'); // Create a new table row
var newCell = document.createElement('td'); // Create a new table cell
var newCell2 = document.createElement('td'); // Create a new table cell
newCell.appendChild(fileName);
newCell2.appendChild(progressBarContainer);
newRow.appendChild(newCell);
newRow.appendChild(newCell2);
progressBarsContainer.appendChild(newRow);
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', function(event) {
if (event.lengthComputable) {
var percent = Math.round((event.loaded / event.total) * 100);
progressBar.style.width = percent + '%';
progressBar.innerHTML = percent + '%';
}
});
xhr.addEventListener('load', function(event) {
var uploadStatus = document.getElementById('uploadStatus');
uploadStatus.innerHTML = event.target.responseText;
// Reset the input field of type "file"
document.getElementById('fileUpload').value = '';
});
xhr.open('POST', 'upload.do', true);
xhr.send(formData);
}
关键点:
- 这里的文件上传是一个一个上传的,上传一个显示一个状态,上传完成后,显示全部文件的状态。
- xhr.open('post', 'upload.do',true); URL需要看本身后台发布的URL,我这里测试的是一个Servlet后台。
3.效果图
上传前
上传后
4.相关章节
HTML批量文件上传1——图像预览方式_html上传多张图片并预览-CSDN博客
完整的项目下载(待后续补充):