欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2024-03-11 11:45:43
一、课程概述 在web应用系统开发中,文件上传功能是非常常用的功能,今天来主要讲讲javaweb中的文件上传功能的相关技术实现,并且随着互联网技术的飞速发展,用户对网站的...

一、课程概述

在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请求内容:

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

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

三、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组件

到webupload官网下载webupload包

webupload目录结构:

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

基本文件上传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.2
uploader.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自动把文件会分几个请求发送给后台

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

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

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

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

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

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()方法计算文件的唯一标记符
//该函数接收一个deferred
beforesendfile:function(file){
//创建一个deffered
var 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.请求后台是否保存过该文件,如果存在,则跳过该文件,实现秒传功能
//返回deffered
return 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){
//创建一个deffered
var 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();
}
}
}
);
});
//返回deffered
return 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文件上传下载实例讲解(酷炫的文件上传技术),希望对大家有所帮助