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

三、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目录结构:

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

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

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

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文件上传下载实例讲解(酷炫的文件上传技术),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!

以上是 JavaWeb文件上传下载实例讲解(酷炫的文件上传技术) 的全部内容, 来源链接: utcz.com/p/213519.html

回到顶部