首页 > 编程 > Java > 正文

JavaWeb文件上传下载实例讲解(酷炫的文件上传技术)

2019-11-26 13:33:03
字体:
来源:转载
供稿:网友

一、课程概述

在Web应用系统开发中,文件上传功能是非常常用的功能,今天来主要讲讲JavaWeb中的文件上传功能的相关技术实现,并且随着互联网技术的飞速发展,用户对网站的体验要求越来越高,在文件上传功能的技术上也出现许多创新点,例如异步上传文件,拖拽式上传,黏贴上传,上传进度监控,文件缩略图,大文件断点续传,大文件秒传等等。

本课程需要的基础知识:

了解基本的Http协议内容

基本IO流操作技术

Servlet基础知识

javascript/jQuery技术基础知识

二、文件上传的基础

对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,并且所有流数据都会随着Http请求携带到服务器端。所以,文件上传时的请求内容格式要能够基本看懂。

文件上传页面:

<form action="/itheimaUpload/UploadServlet" method="post" enctype="multipart/form-data">请选择上传的文件:<input type="file" name="attach"/><br/><input type="submit" value="提交"/></form>

Http请求内容:

文件上传

文件上传

三、Java后台使用Servlet接收文件

如果使用Servlet获取上传文件的输入流然后再解析里面的请求参数是比较麻烦,所以一般后台选择采用Apache的开源工具common-fileupload这个文件上传组件。

//Java后台代码:Commons-fileUpload组件上传文件public class UploadServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {//1.配置缓存DiskFileItemFactory factory = new DiskFileItemFactory(1*1024*1024,new File("c:/tempFiles/"));//2.创建ServleFileUpload对象ServletFileUpload sfu = new ServletFileUpload(factory);//解决文件名称中文问题sfu.setHeaderEncoding("utf-8");//3.解析try {List<FileItem> list = sfu.parseRequest(request);//解析所有内容if(list!=null){for(FileItem item:list){//判断是否为普通表单参数if(item.isFormField()){//普通表单参数//获取表单的name属性名称String fieldName = item.getFieldName();//获取表单参数值String value = item.getString("utf-8"); }else{//文件if(item.getName()!=null && !item.getName().equals("")) {//保存到服务器硬盘 FileUtils.copyInputStreamToFile(item.getInputStream(), new File("c:/targetFiles/"+item.getName()));item.delete();}}}}} catch (FileUploadException e) {e.printStackTrace();}}public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}

四、使用WebUploader上传组件

文件上传页面的前端我们可以选择使用一些比较好用的上传组件,例如百度的开源组件WebUploader,这个组件基本能满足文件上传的一些日常所需功能,如异步上传文件,拖拽式上传,黏贴上传,上传进度监控,文件缩略图,甚至是大文件断点续传,大文件秒传。

下载WebUpload组件

http://fex.baidu.com/webuploader/ 到WebUpload官网下载WebUpload包

WebUpload目录结构:

WebUpload目录结构

基本文件上传Demo(包含上传进度)

前端

1.1 在页面导入所需css,js

<link rel="stylesheet" type="text/css"href="${pageContext.request.contextPath}/css/webuploader.css"><script type="text/javascript"src="${pageContext.request.contextPath }/js/jquery-1.10.2.min.js"></script><script type="text/javascript"src="${pageContext.request.contextPath }/js/webuploader.js"></script>

1.2 编写上传页面标签

<!-- 上传div --><div id="uploader"><!-- 显示文件列表信息 --><ul id="fileList"></ul><!-- 选择文件区域 --><div id="filePicker">点击选择文件</div></div>

1.3 编写webupload代码

<script type="text/javascript">//1.初始化WebUpload,以及配置全局的参数var uploader = WebUploader.create({//flashk控件的地址swf: "${pageContext.request.contextPath}/js/Uploader.swf",//后台提交地址server:"${pageContext.request.contextPath}/UploadServlet",//选择文件控件的标签pick:"#filePicker",//自动上传文件auto:true,});//2.选择文件后,文件信息队列展示// 注册fileQueued事件:当文件加入队列后触发// file: 代表当前选择的文件uploader.on("fileQueued",function(file){//追加文件信息div$("#fileList").append("<div id='"+file.id+"' class='fileInfo'><span>"+file.name+"</span><div class='state'>等待上传...</div><span class='text'></span></div>");});//3.注册上传进度监听//file: 正在上传的文件//percentage: 当前进度的比例。最大为1.例如:0.2uploader.on("uploadProgress",function(file,percentage){var id = $("#"+file.id);//更新状态信息id.find("div.state").text("上传中...");//更新上传百分比id.find("span.text").text(Math.round(percentage*100)+"%");});//4.注册上传完毕监听//file:上传完毕的文件//response:后台回送的数据,以json格式返回uploader.on("uploadSuccess",function(file,response){//更新状态信息$("#"+file.id).find("div.state").text("上传完毕");});

2)后端Servlet代码

DiskFileItemFactory factory = new DiskFileItemFactory();ServletFileUpload sfu = new ServletFileUpload(factory);sfu.setHeaderEncoding("utf-8");try {List<FileItem> items = sfu.parseRequest(request);for(FileItem item:items){if(item.isFormField()){//普通信息}else{//文件信息//判断只有文件才需要进行保存处理System.out.println("接收的文件名称:"+item.getName());//拷贝文件到后台的硬盘FileUtils.copyInputStreamToFile(item.getInputStream(), new File(serverPath+"/"+item.getName()));System.out.println("文件保存成功");}}} catch (FileUploadException e) {e.printStackTrace();}

生成图片缩略图

关键点:调用uploader.makeThumb()方法生成缩略图

uploader.on("fileQueued",function(file){//追加文件信息div$("#fileList").append("<div id='"+file.id+"' class='fileInfo'><img/><span>"+file.name+"</span><div class='state'>等待上传...</div><span class='text'></span></div>");//制造图片缩略图:调用makeThumb()方法//error: 制造缩略图失败//src: 缩略图的路径uploader.makeThumb(file,function(error,src){var id = $("#"+file.id);//如果失败,则显示“不能预览”if(error){id.find("img").replaceWith("不能预览");} //成功,则显示缩略图到指定位置id.find("img").attr("src",src); });});

拖拽,黏贴上传

1)页面添加拖拽区域的div

<!-- 上传div --><div id="uploader"><!-- 文件拖拽区域 --><div id="dndArea"><p>将文件直接拖拽到这里即可自动上传</p></div><!-- 显示文件列表信息 --><ul id="fileList"></ul><!-- 选择文件区域 --><div id="filePicker">点击选择文件</div></div>

2)在webuploader的全局配置参数添加拖拽功能的参数

//1.初始化WebUpload,以及配置全局的参数var uploader = WebUploader.create({//flashk控件的地址swf: "${pageContext.request.contextPath}/js/Uploader.swf",//后台提交地址server:"${pageContext.request.contextPath}/UploadServlet",//选择文件控件的标签pick:"#filePicker",//自动上传文件auto:true,//开启拖拽功能,指定拖拽区域dnd:"#dndArea",//禁用页面其他地方的拖拽功能,防止页面直接打开文件disableGlobalDnd:true//开启黏贴功能paste:"#uploader"});

大文件分块上传

1)在webuploader全局参数中添加分块上传参数

//1.初始化WebUpload,以及配置全局的参数var uploader = WebUploader.create({//flashk控件的地址swf: "${pageContext.request.contextPath}/js/Uploader.swf",//后台提交地址server:"${pageContext.request.contextPath}/UploadServlet",//选择文件控件的标签pick:"#filePicker",//自动上传文件auto:true,//开启拖拽功能,指定拖拽区域dnd:"#dndArea",//禁用页面其他地方的拖拽功能,防止页面直接打开文件disableGlobalDnd:true,//开启黏贴功能paste:"#uploader",//分块上传设置//是否分块上传chunked:true,//每块文件大小(默认5M)chunkSize:5*1024*1024,//开启几个并发线程(默认3个)threads:3,//在上传当前文件时,准备好下一个文件prepareNextFile:true});

2)监控上传文件的三个时间点

添加以上三个配置后,会发现当文件超过5M时,webuploader自动把文件会分几个请求发送给后台

文件上传

每个分块请求,包含的信息:

文件上传

可以监听文件分块上传的三个重要的时间点。

文件上传

before-send-file : 在所有分块发送之前调用 before-send: 如果有分块,在每个分块发送之前调用 after-send-file: 在所有分块发送完成之后调用//5.监控文件上传的三个时间点(注意:该段代码必须放在WebUploader.create之前)//时间点1::所有分块进行上传之前(1.可以计算文件的唯一标记;2.可以判断是否秒传) //时间点2: 如果分块上传,每个分块上传之前(1.询问后台该分块是否已经保存成功,用于断点续传)//时间点3:所有分块上传成功之后(1.通知后台进行分块文件的合并工作)WebUploader.Uploader.register({"before-send-file":"beforeSendFile","before-send":"beforeSend","after-send-file":"afterSendFile"},{//时间点1::所有分块进行上传之前调用此函数beforeSendFile:function(){//1.计算文件的唯一标记,用于断点续传和秒传//2.请求后台是否保存过该文件,如果存在,则跳过该文件,实现秒传功能},//时间点2:如果有分块上传,则 每个分块上传之前调用此函数beforeSend:function(){//1.请求后台是否保存过当前分块,如果存在,则跳过该分块文件,实现断点续传功能},//时间点3:所有分块上传成功之后调用此函数afterSendFile:function(){//1.如果分块上传,则通过后台合并所有分块文件}});

before-send-file逻辑:

//利用md5File()方法计算文件的唯一标记符//该函数接收一个deferredbeforeSendFile:function(file){//创建一个defferedvar deferred = WebUploader.Deferred();//1.计算文件的唯一标记,用于断点续传和秒传(new WebUploader.Uploader()).md5File(file,0,5*1024*1024).progress(function(percentage){$("#"+file.id).find("div.state").text("正在获取文件信息...");}).then(function(val){uniqueFileTag = val;$("#"+file.id).find("div.state").text("成功获取文件信息");//只有文件信息获取成功,才进行下一步操作deferred.resolve();});//alert(uniqueFileTag);//2.请求后台是否保存过该文件,如果存在,则跳过该文件,实现秒传功能//返回defferedreturn deferred.promise();}

before-send逻辑:

//向后台发送当前文件的唯一标记,用于后台创建保存分块文件的目录beforeSend:function(){//携带当前文件的唯一标记到后台,用于让后台创建保存该文件分块的目录this.owner.options.formData.fileMd5 = fileMd5;}

3)后台需要保存所有分块文件

//为每个文件创建一个目录,并保存这个文件的所有分块文件//判断是否已经分块上传if(chunks!=null){System.out.println("分块处理...");//进行分块上传了//建立一个临时目录,用于保存所有分块文件File chunksDir = new File(serverPath+"/"+fileMd5);if(!chunksDir.exists()){chunksDir.mkdir();}if(chunk!=null){//保存分块文件File chunkFile = new File(chunksDir.getPath()+"/"+chunk);FileUtils.copyInputStreamToFile(item.getInputStream(), chunkFile);}

4)前台通知后台合并所有分块文件

//前台通知后台合并文件after-send-file逻辑:afterSendFile:function(file){//1.如果分块上传,则通过后台合并所有分块文件//请求后台合并文件$.ajax({type:"POST",url:"${pageContext.request.contextPath}/UploadCheckServlet?action=mergeChunks",data:{//文件唯一标记fileMd5:fileMd5,//文件名称fileName:file.name},dataType:"json",success:function(response){alert(response.msg);}});}//后台合并所有分块文件if("mergeChunks".equals(action)){System.out.println("开始合并文件...");//合并文件String fileMd5 = request.getParameter("fileMd5");String fileName = request.getParameter("fileName");//读取目录里面的所有文件File f = new File(serverPath+"/"+fileMd5);File[] fileArray = f.listFiles(new FileFilter(){//排除目录,只要文件public boolean accept(File pathname) {if(pathname.isDirectory()){return false;}return true;}});//转成集合,便于排序List<File> fileList = new ArrayList<File>(Arrays.asList(fileArray));//从小到大排序Collections.sort(fileList, new Comparator<File>() {public int compare(File o1, File o2) {if(Integer.parseInt(o1.getName()) < Integer.parseInt(o2.getName())){return -1;}return 1;}});File outputFile = new File(serverPath+"/"+fileName);//创建文件outputFile.createNewFile();//输出流FileChannel outChannel = new FileOutputStream(outputFile).getChannel();//合并FileChannel inChannel;for(File file : fileList){inChannel = new FileInputStream(file).getChannel();inChannel.transferTo(0, inChannel.size(), outChannel);inChannel.close();//删除分片file.delete();}//清除文件夹File tempFile = new File(serverPath+"/"+fileMd5);if(tempFile.isDirectory() && tempFile.exists()){tempFile.delete();}//关闭流outChannel.close();response.setContentType("text/html;charset=utf-8");response.getWriter().write("{/"msg/":/"合并成功/"}");}

大文件断点续传

在实现了分块上传的基础上,实现断点续传就非常简单了!!!

前端:

//时间点2:如果有分块上传,则 每个分块上传之前调用此函数//block:代表当前分块对象beforeSend:function(block){//1.请求后台是否保存过当前分块,如果存在,则跳过该分块文件,实现断点续传功能var deferred = WebUploader.Deferred();//请求后台是否保存完成该文件信息,如果保存过,则跳过,如果没有,则发送该分块内容$.ajax({type:"POST",url:"${pageContext.request.contextPath}/UploadCheckServlet?action=checkChunk",data:{//文件唯一标记fileMd5:fileMd5,//当前分块下标chunk:block.chunk,//当前分块大小chunkSize:block.end-block.start},dataType:"json",success:function(response){if(response.ifExist){//分块存在,跳过该分块deferred.reject();}else{//分块不存在或者不完整,重新发送该分块内容deferred.resolve();}}});//携带当前文件的唯一标记到后台,用于让后台创建保存该文件分块的目录this.owner.options.formData.fileMd5 = fileMd5;return deferred.promise(); },

后台:

//检查该分块是否存在或者完整保存private void checkChunk(HttpServletRequest request,HttpServletResponse response) throws IOException,FileNotFoundException {System.out.println("checkChunk...");String fileMd5 = request.getParameter("fileMd5");String chunk = request.getParameter("chunk");String chunkSize = request.getParameter("chunkSize");File checkFile = new File(serverPath+"/"+fileMd5+"/"+chunk);response.setContentType("text/html;charset=utf-8");//检查文件是否存在,且大小是否一致if(checkFile.exists() && checkFile.length()==Integer.parseInt(chunkSize)){response.getWriter().write("{/"ifExist/":1}");}else{response.getWriter().write("{/"ifExist/":0}");}}

文件秒传

在所有分块请求之前,就已经可以进行实现秒传功能!!!

前端:

beforeSendFile:function(file){//创建一个defferedvar deferred = WebUploader.Deferred();//1.计算文件的唯一标记,用于断点续传和秒传(new WebUploader.Uploader()).md5File(file,0,5*1024*1024).progress(function(percentage){$("#"+file.id).find("div.state").text("正在获取文件信息...");}).then(function(val){fileMd5 = val;$("#"+file.id).find("div.state").text("成功获取文件信息");//2.请求后台是否保存过该文件,如果存在,则跳过该文件,实现秒传功能$.ajax({type:"POST",url:"${pageContext.request.contextPath}/UploadCheckServlet?action=fileCheck",data:{//文件唯一标记fileMd5:fileMd5},dataType:"json",success:function(response){if(response.ifExist){$("#"+file.id).find("div.state").text("秒传成功"); //如果存在,则跳过该文件,秒传成功deferred.reject();}else{//继续上传deferred.resolve();}}});});//返回defferedreturn deferred.promise();},

后台:

//检查文件的md5数据是否跟在数据库存在private void fileCheck(HttpServletRequest request,HttpServletResponse response) throws IOException,FileNotFoundException {String fileMd5 = request.getParameter("fileMd5");//模拟数据库Map<String,String> database = new HashMap<String,String>();database.put("576018603f4091782b68b78af85704a1", "01.课程回顾.itcast");response.setContentType("text/html;charset=utf-8");if(database.containsKey(fileMd5)){response.getWriter().write("{/"ifExist/":1}");}else{response.getWriter().write("{/"ifExist/":0}");}}

以上所述是小编给大家介绍的JavaWeb文件上传下载实例讲解(酷炫的文件上传技术),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对武林网网站的支持!

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表