- 浏览: 1459430 次
- 性别:
- 来自: 广东
文章分类
- 全部博客 (190)
- eclipse (5)
- struts (15)
- spring (1)
- hibernate (0)
- myeclipse (3)
- java (35)
- apache (1)
- PHP (7)
- 虚拟机 (0)
- 网络安全 (3)
- 防火墙 (1)
- linux (7)
- SVN (22)
- 测试文档 (1)
- 测试经验 (0)
- 项目管理 (0)
- BUG管理工具 (3)
- 安装配置 (3)
- 性能工具 (7)
- 脚本学习 (0)
- 协议选择 (0)
- loadrunner错误处理 (0)
- 相关监控配置 (0)
- 框架的认识 (0)
- 手动关联 (0)
- 性能调优 (1)
- 负载均衡 (0)
- 代码学习 (1)
- Windows (5)
- 软件开发安全 (0)
- 考研经验 (1)
- SQL SERVER (1)
- MySQL (6)
- LVS (0)
- ORACLE (1)
- TOMCAT (0)
- 开源框架 (1)
- EOS (3)
- web (5)
- JEECMS (7)
- XML (1)
- LDAP (3)
- ehcache (1)
- Ajax (3)
- OpenSourceTools (1)
- Exception (1)
- 密码学 (1)
- os-centos (1)
- os-ubuntu (0)
- os-FreeBSD (0)
- os-Fedora (0)
- 浏览器-chrome (1)
- flex (1)
- 数据结构与算法 (0)
最新评论
-
joedan0104:
挺方便的,谢谢
JDK1.6官方下载_JDK6官方下载地址:http://www.java.net/download -
naruik:
非常感谢,不用自己找了。收藏和关注了。
JDK1.6官方下载_JDK6官方下载地址:http://www.java.net/download -
scd01234:
感谢!
JDK1.6官方下载_JDK6官方下载地址:http://www.java.net/download -
qingcheng123:
大虾,5.3这个版本有没有下载地址呀,谢谢!
EOS5.3+Tomcat5.0.28升级JDK1.5解决方案 -
1021082712:
JDK1.6官方下载_JDK6官方下载地址:http://www.java.net/download
基于AJAX的文件上传显示进度条实现
文章分类:Web前端 关键字: ajax, fileupload
基于Ajax的文件上传要实现的功能要求,要在用户提交了上传按钮请求后,客户端其页面要显示文件上传进度条。
其整个功能时序图如图所示。
简单的说,要实现在客户端显示进度条,需要做的是:当客户端提交上传文件请求后,服务器在上传文件的过程中,将上传进度情况保存到Session中,客户端周期性的发送请求来获取保存在Session中值,以获取上传文件的进度信息。
1. 新建web工程AjaxUpload。
2. 将commons-fileupload-1.2.1-bin.zip包中的commons-fileupload-1.2.1.jar文件和commons-io-1.4-bin.zip包中的commons-io-1.4.jar文件拷贝到web工程下的WEB-INF\lib目录下。
3. 由于本实例涉及到多个类,处理此类问题最好是给相应的类打包进行管理。在web工程src目录下新建一个包com.ncu.upload。
4. 服务器端实现。
首先要创建一个用来保存文件上传状态的类 FileUploadStatus。其源码如下:
- package com.ncu.upload;
- import java.util.*;
- public class FileUploadStatus {
- //上传总量
- private long uploadTotalSize=0;
- //读取上传总量
- private long readTotalSize=0;
- //当前上传文件号
- private int currentUploadFileNum=0;
- //成功读取上传文件数
- private int successUploadFileCount=0;
- //状态
- private String status="";
- //处理起始时间
- private long processStartTime=0l;
- //处理终止时间
- private long processEndTime=0l;
- //处理执行时间
- private long processRunningTime=0l;
- //上传文件URL列表
- private List uploadFileUrlList=new ArrayList();
- //取消上传
- private boolean cancel=false;
- //上传base目录
- private String baseDir="";
- public String getBaseDir() {
- return baseDir;
- }
- public void setBaseDir(String baseDir) {
- this.baseDir = baseDir;
- }
- public boolean getCancel() {
- return cancel;
- }
- public void setCancel(boolean cancel) {
- this.cancel = cancel;
- }
- public List getUploadFileUrlList() {
- return uploadFileUrlList;
- }
- public void setUploadFileUrlList(List uploadFileUrlList) {
- this.uploadFileUrlList = uploadFileUrlList;
- }
- public long getProcessRunningTime() {
- return processRunningTime;
- }
- public void setProcessRunningTime(long processRunningTime) {
- this.processRunningTime = processRunningTime;
- }
- public long getProcessEndTime() {
- return processEndTime;
- }
- public void setProcessEndTime(long processEndTime) {
- this.processEndTime = processEndTime;
- }
- public long getProcessStartTime() {
- return processStartTime;
- }
- public void setProcessStartTime(long processStartTime) {
- this.processStartTime = processStartTime;
- }
- public long getReadTotalSize() {
- return readTotalSize;
- }
- public void setReadTotalSize(long readTotalSize) {
- this.readTotalSize = readTotalSize;
- }
- public int getSuccessUploadFileCount() {
- return successUploadFileCount;
- }
- public void setSuccessUploadFileCount(int successUploadFileCount) {
- this.successUploadFileCount = successUploadFileCount;
- }
- public int getCurrentUploadFileNum() {
- return currentUploadFileNum;
- }
- public void setCurrentUploadFileNum(int currentUploadFileNum) {
- this.currentUploadFileNum = currentUploadFileNum;
- }
- public String getStatus() {
- return status;
- }
- public void setStatus(String status) {
- this.status = status;
- }
- public long getUploadTotalSize() {
- return uploadTotalSize;
- }
- public void setUploadTotalSize(long uploadTotalSize) {
- this.uploadTotalSize = uploadTotalSize;
- }
- }
package com.ncu.upload; import java.util.*; public class FileUploadStatus { //上传总量 private long uploadTotalSize=0; //读取上传总量 private long readTotalSize=0; //当前上传文件号 private int currentUploadFileNum=0; //成功读取上传文件数 private int successUploadFileCount=0; //状态 private String status=""; //处理起始时间 private long processStartTime=0l; //处理终止时间 private long processEndTime=0l; //处理执行时间 private long processRunningTime=0l; //上传文件URL列表 private List uploadFileUrlList=new ArrayList(); //取消上传 private boolean cancel=false; //上传base目录 private String baseDir=""; public String getBaseDir() { return baseDir; } public void setBaseDir(String baseDir) { this.baseDir = baseDir; } public boolean getCancel() { return cancel; } public void setCancel(boolean cancel) { this.cancel = cancel; } public List getUploadFileUrlList() { return uploadFileUrlList; } public void setUploadFileUrlList(List uploadFileUrlList) { this.uploadFileUrlList = uploadFileUrlList; } public long getProcessRunningTime() { return processRunningTime; } public void setProcessRunningTime(long processRunningTime) { this.processRunningTime = processRunningTime; } public long getProcessEndTime() { return processEndTime; } public void setProcessEndTime(long processEndTime) { this.processEndTime = processEndTime; } public long getProcessStartTime() { return processStartTime; } public void setProcessStartTime(long processStartTime) { this.processStartTime = processStartTime; } public long getReadTotalSize() { return readTotalSize; } public void setReadTotalSize(long readTotalSize) { this.readTotalSize = readTotalSize; } public int getSuccessUploadFileCount() { return successUploadFileCount; } public void setSuccessUploadFileCount(int successUploadFileCount) { this.successUploadFileCount = successUploadFileCount; } public int getCurrentUploadFileNum() { return currentUploadFileNum; } public void setCurrentUploadFileNum(int currentUploadFileNum) { this.currentUploadFileNum = currentUploadFileNum; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public long getUploadTotalSize() { return uploadTotalSize; } public void setUploadTotalSize(long uploadTotalSize) { this.uploadTotalSize = uploadTotalSize; } }
由于要在客户端要显示进度条,所以在上传过程中服务器端需要监视和维护上传状态的信息,此过程需要处理的数据信息是:不断更新Session中保存的FileUploadStatus实例的信息,如:已经上传的字节数,上传文件的总大小等。FileUpload现在的1.2版本为监视上传进度提供了内建的支持,可以直接继承类ProgressListener,然后重载update()方法,在该方法中添加自己要处理的代码,最后在文件上传处理代码(后面会讲到)中通过为ServletFileUpload对象注册创建的监听类。监听类UploadListener的源代码如下:
- package com.ncu.upload;
- import javax.servlet.http.HttpSession;
- import org.apache.commons.fileupload.ProgressListener;
- public class UploadListener implements ProgressListener {
- private HttpSession session=null;
- public UploadListener (HttpSession session){
- this.session=session;
- }
- /**
- * 更新状态
- * @param pBytesRead 读取字节总数
- * @param pContentLength 数据总长度
- * @param pItems 当前正在被读取的field号
- */
- public void update(long pBytesRead, long pContentLength, int pItems) {
- FileUploadStatus fuploadStatus = UploadServlet.takeOutFileUploadStatusBean(this.session);
- fuploadStatus.setUploadTotalSize(pContentLength);
- //读取完成
- if (pContentLength == -1) {
- fuploadStatus.setStatus("完成对" + pItems + "个文件的读取:读取了 " + pBytesRead + "/" + pContentLength+ " bytes.");
- fuploadStatus.setReadTotalSize(pBytesRead);
- fuploadStatus.setCurrentUploadFileNum(pItems);
- fuploadStatus.setProcessEndTime(System.currentTimeMillis());
- fuploadStatus.setProcessRunningTime(fuploadStatus.getProcessEndTime());
- }else{//读取过程中
- fuploadStatus.setStatus("当前正在处理第" + pItems+"个文件:已经读取了 " + pBytesRead + " / " + pContentLength+ " bytes.");
- fuploadStatus.setReadTotalSize(pBytesRead);
- fuploadStatus.setCurrentUploadFileNum(pItems);
- fuploadStatus.setProcessRunningTime(System.currentTimeMillis());
- }
- //System.out.println("已经读取:" + pBytesRead);
- UploadServlet.storeFileUploadStatusBean(this.session, fuploadStatus);
- }
- }
package com.ncu.upload; import javax.servlet.http.HttpSession; import org.apache.commons.fileupload.ProgressListener; public class UploadListener implements ProgressListener { private HttpSession session=null; public UploadListener (HttpSession session){ this.session=session; } /** * 更新状态 * @param pBytesRead 读取字节总数 * @param pContentLength 数据总长度 * @param pItems 当前正在被读取的field号 */ public void update(long pBytesRead, long pContentLength, int pItems) { FileUploadStatus fuploadStatus = UploadServlet.takeOutFileUploadStatusBean(this.session); fuploadStatus.setUploadTotalSize(pContentLength); //读取完成 if (pContentLength == -1) { fuploadStatus.setStatus("完成对" + pItems + "个文件的读取:读取了 " + pBytesRead + "/" + pContentLength+ " bytes."); fuploadStatus.setReadTotalSize(pBytesRead); fuploadStatus.setCurrentUploadFileNum(pItems); fuploadStatus.setProcessEndTime(System.currentTimeMillis()); fuploadStatus.setProcessRunningTime(fuploadStatus.getProcessEndTime()); }else{//读取过程中 fuploadStatus.setStatus("当前正在处理第" + pItems+"个文件:已经读取了 " + pBytesRead + " / " + pContentLength+ " bytes."); fuploadStatus.setReadTotalSize(pBytesRead); fuploadStatus.setCurrentUploadFileNum(pItems); fuploadStatus.setProcessRunningTime(System.currentTimeMillis()); } //System.out.println("已经读取:" + pBytesRead); UploadServlet.storeFileUploadStatusBean(this.session, fuploadStatus); } }
有了前面两个类的基础,下来我们可以动手去实现真正处理整个操作Servlet类。源代码如下。
- package com.ncu.upload;
- import java.io.*;
- import java.util.List;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import javax.servlet.http.HttpSession;
- import org.apache.commons.fileupload.FileItem;
- import org.apache.commons.fileupload.FileUploadException;
- import org.apache.commons.fileupload.disk.DiskFileItemFactory;
- import org.apache.commons.fileupload.servlet.*;
- /**
- * Servlet implementation class for Servlet: UploadServlet
- *
- */
- public class UploadServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
- static final long serialVersionUID = 1L;
- public static final String UPLOAD_STATUS="UPLOAD_STATUS";
- public static final String UPLOAD_DIR="/upload";
- public UploadServlet() {
- super();
- }
- /**
- * 从文件路径中取出文件名
- * @param filePath
- * @return
- */
- private String takeOutFileName(String filePath){
- int pos=filePath.lastIndexOf(File.separator);
- if (pos>0){
- return filePath.substring(pos+1);
- }
- else{
- return filePath;
- }
- }
- /**
- * 从request中取出FileUploadStatus Bean
- * @param request
- * @return
- */
- public static FileUploadStatus takeOutFileUploadStatusBean(HttpSession session){
- Object obj=session.getAttribute(UPLOAD_STATUS);
- if (obj!=null){
- return (FileUploadStatus)obj;
- }
- else{
- return null;
- }
- }
- /**
- * 把FileUploadStatus Bean保存到session
- * @param request
- * @param uploadStatusBean
- */
- public static void storeFileUploadStatusBean(
- HttpSession session,
- FileUploadStatus uploadStatusBean){
- session.setAttribute(UPLOAD_STATUS,uploadStatusBean);
- }
- /**
- * 删除已经上传的文件
- * @param request
- */
- private void deleteUploadedFile(HttpServletRequest request){
- FileUploadStatus fUploadStatus=takeOutFileUploadStatusBean(request.getSession());
- for(int i=0;i<fUploadStatus.getUploadFileUrlList().size();i++){
- File uploadedFile = new File(request.getRealPath(UPLOAD_DIR)+
- File.separator+fUploadStatus.getUploadFileUrlList().get(i));
- uploadedFile.delete();
- }
- fUploadStatus.getUploadFileUrlList().clear();
- fUploadStatus.setStatus("删除已上传的文件");
- storeFileUploadStatusBean(request.getSession(),fUploadStatus);
- }
- /**
- * 上传过程中出错处理
- * @param request
- * @param errMsg
- * @throws IOException
- * @throws ServletException
- */
- private void uploadExceptionHandle(
- HttpServletRequest request,
- String errMsg) throws ServletException, IOException{
- //首先删除已经上传的文件
- deleteUploadedFile(request);
- FileUploadStatus fUploadStatus=takeOutFileUploadStatusBean(request.getSession());
- fUploadStatus.setStatus(errMsg);
- storeFileUploadStatusBean(request.getSession(),fUploadStatus);
- }
- /**
- * 初始化文件上传状态Bean
- * @param request
- * @return
- */
- private FileUploadStatus initFileUploadStatusBean(HttpServletRequest request){
- FileUploadStatus fUploadStatus=new FileUploadStatus();
- fUploadStatus.setStatus("正在准备处理");
- fUploadStatus.setUploadTotalSize(request.getContentLength());
- fUploadStatus.setProcessStartTime(System.currentTimeMillis());
- fUploadStatus.setBaseDir(request.getContextPath()+UPLOAD_DIR);
- return fUploadStatus;
- }
- /**
- * 处理文件上传
- * @param request
- * @param response
- * @throws IOException
- * @throws ServletException
- */
- private void processFileUpload(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
- DiskFileItemFactory factory = new DiskFileItemFactory();
- //设置内存阀值,超过后写入临时文件
- //factory.setSizeThreshold(10240000*5);
- //设置临时文件存储位置
- //factory.setRepository(new File(request.getRealPath("/upload/temp")));
- ServletFileUpload upload = new ServletFileUpload(factory);
- //设置单个文件的最大上传size
- //upload.setFileSizeMax(10240000*5);
- //设置整个request的最大size
- //upload.setSizeMax(10240000*5);
- //注册监听类
- upload.setProgressListener(new UploadListener(request.getSession()));
- //保存初始化后的FileUploadStatus Bean
- storeFileUploadStatusBean(request.getSession(),initFileUploadStatusBean(request));
- try {
- List items = upload.parseRequest(request);
- //处理文件上传
- for(int i=0;i<items.size();i++){
- FileItem item=(FileItem)items.get(i);
- //取消上传
- if (takeOutFileUploadStatusBean(request.getSession()).getCancel()){
- deleteUploadedFile(request);
- break;
- }
- //保存文件
- else if (!item.isFormField() && item.getName().length()>0){
- String fileName=takeOutFileName(item.getName());
- File uploadedFile = new File(request.getRealPath(UPLOAD_DIR)+File.separator+fileName);
- item.write(uploadedFile);
- //更新上传文件列表
- FileUploadStatus fUploadStatus=takeOutFileUploadStatusBean(request.getSession());
- fUploadStatus.getUploadFileUrlList().add(fileName);
- storeFileUploadStatusBean(request.getSession(),fUploadStatus);
- Thread.sleep(500);
- }
- }
- } catch (FileUploadException e) {
- e.printStackTrace();
- //uploadExceptionHandle(request,"上传文件时发生错误:"+e.getMessage());
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- //uploadExceptionHandle(request,"保存上传文件时发生错误:"+e.getMessage());
- }
- }
- /**
- * 回应上传状态查询
- * @param request
- * @param response
- * @throws IOException
- */
- private void responseFileUploadStatusPoll(HttpServletRequest request,HttpServletResponse response) throws IOException{
- FileUploadStatus fUploadStatus=(FileUploadStatus)request.getSession().getAttribute(UPLOAD_STATUS);
- //计算上传完成的百分比
- long percentComplete = (long)Math.floor(((double) fUploadStatus.getReadTotalSize()/(double) fUploadStatus.getUploadTotalSize())*100.0);
- System.out.println("com:"+percentComplete);
- response.setContentType("text/xml");
- response.setCharacterEncoding("UTF-8");
- response.setHeader("Cache-Control", "no-cache");
- if ( ((long)fUploadStatus.getReadTotalSize() == (long)fUploadStatus.getUploadTotalSize()) || (fUploadStatus.getCancel() == true)){
- response.getWriter().write(fUploadStatus.getStatus().toString()+"success");
- }else{
- response.getWriter().write(fUploadStatus.getStatus().toString()+"<div class=\"prog-border\"><div class=\"prog-bar\" style=\"width: "
- + percentComplete + "%;\"></div></div>");
- }
- }
- /**
- * 处理取消文件上传
- * @param request
- * @param response
- * @throws IOException
- */
- private void processCancelFileUpload(HttpServletRequest request,HttpServletResponse response) throws IOException{
- FileUploadStatus fUploadStatus=(FileUploadStatus)request.getSession().getAttribute(UPLOAD_STATUS);
- fUploadStatus.setCancel(true);
- request.getSession().setAttribute(UPLOAD_STATUS, fUploadStatus);
- responseFileUploadStatusPoll(request,response);
- }
- /**
- * 在上传文件列表中查找与文件名相关的id
- * @param request
- * @param fileName 文件名
- * @return 找到返回id,否则返回-1
- */
- private int findFileIdInFileUploadedList(HttpServletRequest request,String fileName){
- FileUploadStatus fileUploadStatus=takeOutFileUploadStatusBean(request.getSession());
- for(int i=0;i<fileUploadStatus.getUploadFileUrlList().size();i++){
- if (fileName.equals((String)fileUploadStatus.getUploadFileUrlList().get(i))){
- return i;
- }
- }
- return -1;
- }
- protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- doPost(request,response);
- }
-
<
评论
2 楼 943366739bin 2011-11-25不过同时上传几个文件的时候,只能显示整体的进度!
有没有办法可以显示每个文件的上传进度啊?1 楼 943366739bin 2011-11-25给力啊!!发表评论
相关推荐
在这个"struts+jQuery+ajax文件上传带进度条"的项目中,我们主要关注的是如何利用这些技术实现在后台处理文件上传时,前端能够显示实时的进度条效果,提升用户体验。 首先,Struts是一个基于MVC(Model-View-...
在基于Ajax的文件上传中,通常需要通过监听XMLHttpRequest对象的onprogress事件来获取上传进度,然后更新前端的进度条显示。这样,用户可以直观地看到文件上传的进度,提高了操作的透明度和用户的耐心。 【文件上传...
AJAXFileUpload是一种基于AJAX技术的异步文件上传组件,它允许用户在不刷新页面的情况下上传文件,并且可以实时显示上传进度,提供良好的用户体验。本文将深入探讨AJAXFileUpload的工作原理、实现方式以及其在实际...
在IT行业中,构建一个能够实时显示文件上传进度的交互式用户界面是一项常见的需求。本教程将探讨如何利用Spring、Ajax以及Velocity技术来实现这一功能,为用户提供更好的用户体验。 首先,让我们了解一下这三个关键...
下面给大家分享下基于ajax实现文件上传并显示进度条。在jsp部分,需要设计一个表单,form的属性添加 enctype=”multipart/form-data”,设计一个iframe,作为隐藏。form的target等于iframe的name; 在servlet部分:...
结合进度条展示,可以提供更优秀的用户体验,让用户清晰地看到文件上传的进度,消除等待的不确定感。 首先,要实现Ajax无刷新上传文件,我们需要了解XMLHttpRequest对象,这是Ajax的核心组件。通过创建...
本示例是基于VS2010的.NET源代码,无需额外的DLL封装,实现了大文件分块上传,并通过Ajax技术实时显示上传进度条。 首先,让我们了解一下大文件上传的基本原理。传统的文件上传方式通常限制了单个文件的大小,对于...
为了提供良好的用户体验,开发者通常会引入进度条控件来指示文件上传进度,让用户知道操作何时完成。这个“上传大文件并显示进度条控件实例”是一个典型的案例,它涉及的技术主要包括前端的文件API、Ajax异步通信...
这样在其他页面中,只需要插入这个标签,就可以实现文件上传并显示进度条。 在实际应用中,这样的文件上传功能对于用户来说非常友好,因为它提供了实时的反馈,提高了用户体验。同时,SSH框架的使用确保了代码的...
该压缩包"php单文件实现ajax多文件上传带进度条 v20211024.zip"包含了一个简单的PHP和AJAX实现的多文件上传系统,特别的是它还带有文件上传进度条的功能。这个解决方案可能对那些正在寻找如何在Web应用中实现高效且...
2. **AJAXFileUpload**: AJAXFileUpload是基于jQuery的一个插件,用于实现异步文件上传。它利用Ajax技术,使得文件上传无需刷新页面即可完成。这极大地提升了用户体验,因为用户可以在上传文件的同时继续浏览其他...
标题 "ssh2(struts2+spring2.5+hibernate3.3+ajax)带进度条文件上传(封装成标签)" 涉及到的是一个基于Java Web的项目,利用了Struts2、Spring2.5、Hibernate3.3和Ajax技术,实现了文件上传并带有进度条显示的功能...
**Ajax上传组件带进度条**是一种在Web应用中实现文件上传功能的方式,它结合了Ajax技术,能够在不刷新整个页面的情况下实现文件的异步上传,并且提供了可视化的进度条显示,提高了用户体验。这个组件是基于Struts2...
本项目聚焦于一个基于AJAX(异步JavaScript和XML)与JSP(JavaServer Pages)技术实现的带进度条的文件上传系统,提供了一种优化的用户体验,通过实时反馈文件上传进度来提高交互性。 首先,我们需要理解AJAX的核心...
"Ajax异步上传(带进度条)"就是一种能够提供流畅用户体验的上传方式,它允许用户在不刷新整个页面的情况下进行文件上传,并通过进度条实时显示上传进度。这种方式相比传统的表单提交方式,用户体验更佳,因为它避免...
6. **进度条实现**:为了显示上传进度,开发者需要服务器端定期更新文件上传的进度信息,并通过AJAX回调返回给客户端。客户端则根据这些信息更新进度条的状态。这通常涉及到浏览器的事件监听,如`onprogress`事件,...
综上所述,这个源码包提供了一个跨平台、多语言的实现,利用AJAX技术实现了无刷新的文件上传,并且带有进度条显示,对于开发Web应用尤其是需要处理大文件上传的场景非常有价值。同时,它还考虑了不同开发环境和工具...
在Struts中,可以实现单文件和多文件的上传,并且为了提升用户体验,我们还可以添加进度条来显示文件上传的状态。本文将详细介绍如何在Struts中实现这两个功能,并带上传进度条。 首先,我们需要了解Struts中处理...
"ASP.NET(c#)文件上传显示进度条"是一个高级的实现,它不仅涵盖了基础的文件上传操作,还包括了用户友好的进度条展示,使得用户可以实时看到文件上传的状态,提升用户体验。下面我们将详细探讨这一主题。 首先,...