JavaWeb20-文件上传;下载(Java真正的全栈开发)

文件上传&下载一.文件上传

1. 文件上传介绍

要将客户端(浏览器)大数据存储到服务器端,不将数据直接存储到数据库中,而是要将数据存储到服务器所在的磁盘上,这就要使用文件上传。

作用:减少了数据库服务器的压力,对数据的操作更加灵活

2. 文件上传原理分析

所谓的文件上传就是服务器端通过request对象获取输入流,将浏览器端上传的数据读取出来,保存到服务器端

浏览器端操作

1.请求方式必须是post

2.使用<input type=’file’ name=’xxx’>,必须有name属性且有值

3.表单必须设置encType=”multipart/form-data” (ajax见过enctype)

服务器端操作

通过request对象,获取inputStream,就可以将浏览器提交的所有数据读取到.

注意:

这时候,获取其他上传参数的时候,通过request.getParameter(“”)获取不到了.

3. Commons-fileupload介绍与入门案例

commons-fileupload介绍

Apache 开源组织提供了一个用来处理表单文件上传的一个开源组件( Commons-fileupload ),该组件性能优异,并且其API使用极其简单,可以让开发人员轻松实现web文件上传功能,因此在web开发中实现文件上传功能,通常使用Commons-fileupload组件实现。

使用Commons-fileupload组件实现文件上传,需要导入该组件相应的支撑jar包:

Commons-fileupload和commons-io。

commons-io 不属于文件上传组件的开发jar文件,但Commons-fileupload 组件从1.1 版本开始,它工作时需要commons-io包的支持。

commons-fileupload API简单介绍

DiskFileItemFactory,用于设置缓存大小及临时文件存储位置

ServletFileUpload,真正用于文件上传的核心类

FIleItem,代表的是一个上传项

使用步骤:

1.导入jar包(commons-fileupload-1.2.1.jar和commons-io-1.4.jar)

2.创建一个磁盘文件项工厂

DiskFileItemFactory fatory=new DiskFileItemFactory()

3.创建文件上传核心对象

ServletFileUpload upload=new SerlvetFileUpload(fatory)

4.核心对象解析request

List<FileItem> list=upload.parseReqeust(request);

5.遍历list获取里面的内容

for (FileItem f : list) {
if(!f.isFormField()){
InputStream is = f.getInputStream();
/*byte[] b=new byte[1024];
intlen=-1;
FileOutputStream os = new FileOutputStream(new File("f:/1.txt"));
while((len=is.read(b))!=-1){
os.write(b);
}
os.close();
is.close();
*/
FileOutputStream os = new FileOutputStream(new File("c:/1.txt"));
IOUtils.copy(is, os);
os.close();
is.close();
}
}

入门案例分析

在浏览器端创建一个可以上传文件的表单,在服务器端通过commons-fileupload完成文件上传。

浏览器端注意三件事情:

表单的提交方式为post

在表单上添加属性 encType=”multipart/form-data”

使用<input type=’file’ >,添加name属性且有值

服务器端:

创建DiskFileItemFactory

创建ServletFileUpload

通过ServletFileUpload的parseRequest方法得到所有的FileItem

入门案例实现

浏览器端

<form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
<input type="file" name="f">
<input type="submit" value="上传">
</form>

服务器端

// 1.创建一个 DiskFileItemFactory

DiskFileItemFactory factory = new DiskFileItemFactory();

// 2.创建一个ServletFileUpload

ServletFileUpload upload = new ServletFileUpload(factory);

// 3.完成上传操作

try {

// 3.1 得到所有上传项

List<FileItem> items = upload.parseRequest(request);

// 3.2遍历上传项

for (FileItem item : items) {

// 3.3判断是否是上传组件

if (!item.isFormField()) {

// 3.4 将文件真正上传

IOUtils.copy(item.getInputStream(), new FileOutputStream(
"f:/upload/a.txt"));
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}

4. Commons-fileupload详解-DiskFileItemFactory

:磁盘文件项工厂

常用方法:

new DiskFileItemFactory();构造方法

new DiskFileItemFactory(sizeThreshold, repository);构造方法

设置缓存大小

factory.setSizeThreshold(1024*1024); //设置为1m 默认是10k 单位为byte

设置临时文件存储位置

File temp=new File(this.getServletContext().getRealPath("/temp"));

factory.setRepository(file); //可以指定临时文件存储位置,默认是系统的临时文件存储位置(设置缓冲区位置)

5. Commons-fileupload详解-ServletFileUpload

:文件上传核心对象

new ServletFileUpload(factory);

parseRequest方法

List<FileItem> pareRequest(HttpServletRequest request)

得到所有的上传信息,将每一部分映射成FileItem对象.

isMultipartContent方法

boolean isMultipartContent(HttpServletRequest request)

这个方法返回值是boolean,它是用于判断当前表单是否是一个上传的表单,

简单说,就判断它的encType的值是否是 multipart/form-data.

若使用了enctype=multipart/form-data,在后台就不可以使用request.getParameter(name)

upload.setHeaderEncoding(“utf-8”)方法 设置文件名称的编码

用于解决上传文件名称中文乱码问题

设置上传文件大小

void setFileSizeMax(long fileSizeMax) 设置单个文件上传大小

void setSizeMax(long sizeMax) 设置总文件上传大小

6. Commons-fileupload详解-FileItem

:文件项

isFormField方法

这个方法返回的是boolean类型,

它是判断当前组件是否是上传组件 简单说,就是判断type="file",

如果返回true,代表不是上传组件,返回false,代表是上传组件

getName方法

获取上传组件的上传文件的名称,如果是非上传组件,返回的是null

getFieldName方法

获取组件名称,简单说,就是表单的元素的name值。(获取前台页面的name属性)

getString方法

获取非上传组件的value值,(获取的是表单填写的内容)

通过它也可以获取上传的文件内容,但是,使用它获取不合适。

如果使用了commons-fileupload进行文件上传,而上传表单中包含了非上传组件,获取 其值,不能使用request获取.

getString()有一个重载的方法 getString(String encoding)

getString(utf-8)可以解决乱 码问题

getInputStream方法

通过FileItem.getInputStream();可以获取一个输入流,这个输入流就可以

读取出上传文件内容。

InputStream is = item.getInputStream(); // 用于读取上传文件内容的输入流.

FileOutputStream fos = new FileOutputStream("f:/upload/" + filename);
int len = -1;
byte[] b = new byte[1024 * 10];
while ((len = is.read(b)) != -1) {
fos.write(b, 0, len);
fos.flush();
}
fos.close();
is.close();

可以使用IOUtils工具完成文件copy操作

IOUtils.copy(is, fos);

delete()方法

它是用于上传完成后,删除临时文件的

7. 多文件上传

我们在写邮件中可以添加多个附件,那么我们在文件上传时,是不是也可以上传多个文件哪,答案是一定的,那么怎样实现多个文件上传哪?

我们可以通过js实现浏览器端的上传文件框的动态添加。服务器端代码不需要改变.

function addFile() {

//1.得到id叫content的div

var div=document.getElementById("content");

//2.向其中添加一段html代码

div.innerHTML+="<div><input type='file' name='f'><input type='button' value='remove File' onclick='removeFile(this);'></div>";
}
function removeFile(btn){
document.getElementById("content").removeChild(btn.parentNode);
}
///////////////////////////////////////////////////////////////////////////
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<script type="text/javascript">
//添加一个上传框
functionaddFile(){
//获取添加位置
varobj=document.getElementById("fileDiv");
//添加文件筐
obj.innerHTML+=("<div><input type='file' name='file'><input type='button' value='删除' onclick='delFile(this)'/></div>");
}
//删除当前文件上传框
functiondelFile(obj){
document.getElementById("fileDiv").removeChild(obj.parentNode);
}
</script>
<body>
<input type='button' value='添加文件' onclick="addFile()">
<form action="${pageContext.request.contextPath }/upload4" method='post' enctype="multipart/form-data">
<input type="file" name="file"><br>
<div id='fileDiv'></div>
<input type='submit' >
</form>
</body>
</html>

8. 文件上传问题-文件重名

每一个客户端都可以进行文件上传操作,那么当我们上传的文件过多,一定会出现同名的文件,那么在服务器端只能保存一个,对于这个问题,我们在上传文件时,就需要考虑文件重名问题.

一般情况下,对于上传文件,为了保证不重名,会给文件起一个随机名.

一种方案是使用uuid.

一种方案是使用毫秒值

9. 文件上传问题-存储位置

本质就是上传的文件是否允许浏览器端直接访问。

例如:商品添加时需要一个图片,这个图片一定是可以直接被浏览器端访问的。

1.允许被浏览器端访问:放置在WebRoot下,但不能是WEB-INF或META-INF下其及子目录下

2.不允许被浏览器端访问:

若放在工程下则放置在WEB-INF或META-INF及其子目录下.

也可以不放在工程下

10. 文件上传问题-目录分离

当我们上传文件过多,并且保存在同一个目录下时,我们就需要考虑怎样处理它们,因为一个目录下文件过多,不仅降低性能,操作时也不方便。

为了防止同一个目录下方上传文件数量过多

1) 按照上传时间进行目录分离 (周、月 )

2) 按照上传用户进行目录分离 ----- 为每个用户建立单独目录

3) 按照固定数量进行目录分离 ------ 假设每个目录只能存放3000个文件 ,每当一个目录存满3000个文件后,创建一个新的目录

4).按照唯一文件名的hashcode 进行目录分离

public static String generateRandomDir(String uuidFileName) {
// 获得唯一文件名的hashcode
int hashcode = uuidFileName.hashCode();
// 获得一级目录
int d1 = hashcode & 0xf;
// 获得二级目录
int d2 = (hashcode >>> 4) & 0xf;
return "/" + d2 + "/" + d1;// 共有256目录
}

11. 案例-添加商品信息(上传商品图片)

1.导入jar包

2.添加一个\

3.在数据库中添加一个字段 String imgurl

Alter table products add imgurl varchar(100)

4.在javabean中添加一个字段 String imgurl

5.add

二.文件下载

1. 下载介绍与超链接下载

所谓的下载,其实就是将服务器端的资源通过io流写回到浏览器端。

超链接实现下载

如果文件可以直接被浏览器解析,会直接在浏览器上打开。

如果不能解析,可以下载

通过另存为进行下载

这种下载方式:当路径提交时,会通过缺省的servlet将文件直接写回到浏览器端

超链接下载问题分析

原因是在http响应头中content-type,如果它的值可以被浏览器解析,那么响应回的内容就会被浏览器端直接解析,如果content-type的值,不可以被浏览器直接解析,那么就会下载。

注意:不能被浏览器直接访问的文件,不可以使用超连接下载.

2. 下载操作-服务器端编码实现下载

服务器端编码下载原理分析

通过response可以获取输出流,我们将要下载的资源,通过response获取的输出流直接写回到浏览器端就可以。

服务器端下载两个响应头设置(下载步骤)

1.通过response.setContentType()正设置响应数据的mimeType类型.

获取一个文件的mimeType类型

ServletContext.getMimeType(String filename);

2.再添加一个头信息,下载的时候就可以弹出一个提示框了

response.setHeader("Content-Disposition","attachment;filename=下载文件名");

3. 关于下载时乱码问题分析与解决

乱码分析:

对于下载时,我们在显示下载文件名称时,如果包含了中文,就可能出现乱码问题,出现的原因,是对于不同的浏览器,它们在处理下载文件时的编码不一致,ie浏览器使用的是utf-8编码,而firefox浏览器使用的是base64编码

解决方案1:根据浏览器类型

下载文件名称中文乱码问题

response.setHeader("content-disposition", "attachment;filename="+ filename); 这段代码中的filename是指定下载文件时的名称

对于IE浏览器、谷歌,它要求必须给一个UTF-8码

对于firefox浏览器,它要求必须给一个base64编码

代码如下:
if (agent.contains("MSIE")) {
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
// 火狐浏览器
BASE64Encoder base64Encoder = new BASE64Encoder();
filename = "=?utf-8?B?"+ base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}

解决方案2:简单处理

response.setHeader( "Content-Disposition", "attachment;filename=" + new String( fileName.getBytes("gb2312"), "ISO8859-1" ) );

4. 案例-下载销售榜单分析

我们下载的文件的格式是csv.它是一种以”.”来分隔的文件,可以使用excel打开

Excel要求它的文件内容必须是gbk(gb2312)(gb18030),也就是我们可以通过response.setCharacterEncoding(“gbk”)来设置响应数据的编码

5. 案例-下载销售榜单实现

servlet写法:

sql的写法

SELECT
products.name,SUM(buynum) total
FROM
orders,orderitem,products
WHERE
orders.id=orderitem.order_id
AND
orderitem.product_id=products.id
AND
orders.paystat=1
GROUP BY
products.name
ORDER BY
total DESC;

原文发布于微信公众号 - Java帮帮(javahelp)

原文发表时间:2017-02-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏机器学习算法与Python学习

Python:爬虫系列笔记(4) -- URL异常处理

大家好,本节在这里主要说的是URLError还有HTTPError,以及对它们的一些处理。 1.URLError 首先解释下URLError可能产生的原因: 网...

3839
来自专栏Elasticsearch实验室

Elasticsearch 底层系列之分片恢复解析

我们是基础架构部,腾讯云 CES/CTSDB 产品后台服务的支持团队,我们拥有专业的ES开发运维能力,为大家提供稳定、高性能的服务,欢迎有需求的童鞋接入...

7665
来自专栏时序数据库专栏

Elasticsearch 底层系列之分片恢复解析

    我们是基础架构部,腾讯云 CES/CTSDB 产品后台服务的支持团队,我们拥有专业的ES开发运维能力,为大家提供稳定、高性能的服务,欢迎有需求的童鞋接入...

1855
来自专栏决胜机器学习

《Redis设计与实现》读书笔记(十七) ——Redis时间事件与事件调度 (原创内容,转载请注明来源,谢谢)

《Redis设计与实现》读书笔记(十七) ——Redis时间事件与事件调度 (原创内容,转载请注明来源,谢谢) 一、时间事件 1、概述 re...

5044
来自专栏步履前行

Spring Retry

  在我们的业务场景中,经常要调用其他的API来获取信息,比如我们的业务场景需要依赖个人信息来处理,这个时候调用个人信息服务的API,但是由于可能同一时段多方在...

4093
来自专栏向治洪

Android 数据库框架ormlite

Android 数据库框架ormlite 使用精要 前言 本篇博客记录一下笔者在实际开发中使用到的一个数据库框架,这个可以让我们快速实现数据库操作,避免频...

2418
来自专栏程序员八阿哥

王老板Python面试(9):整理的最全 python常见面试题(基本必考)

1)迭代器是一个更抽象的概念,任何对象,如果它的类有next方法和iter方法返回自己本身。对于string、list、dict、tuple等这类容器对象,使用...

2041
来自专栏北京马哥教育

29 条运维工程师必会实用 Linux 命令

虽然Linux发行版支持各种各样的饿GUI(graphical user interfaces),但在某些情况下,Linux的命令行接口(bash)仍然是简单...

3219
来自专栏腾讯云Elasticsearch Service

Elasticsearch 底层系列之分片恢复解析

我们是基础架构部,腾讯云 CES/CTSDB 产品后台服务的支持团队,我们拥有专业的ES开发运维能力,为大家提供稳定、高性能的服务,欢迎有需求的童鞋接入,同时也...

8.4K0
来自专栏闵开慧

tomcat6.0下找不到jasper-runtime.jar

今天有点需求,需要用jasper-runtime.jar包。但是我在我的\apache-tomcat-6.0.16\lib目录下,怎么也找不到这个jar包。结果...

3405

扫码关注云+社区

领取腾讯云代金券