java web 上传附件_Java web文件上传下载

[版权申明:本文系作者原创,转载请注明出处]

文章出处:http://blog.csdn.net/sdksdk0/article/details/52048666

作者:朱培 ID:sdksdk0 邮箱: zhupei@tianfang1314.cn

本文主要从javaweb上传文件到服务器中,并且在服务器端进行数据文件存储,主要分享了文件上传原理、使用第三方开源工具进行上传以及一些文件上传时需要注意的地方,文件的优化处理,还有简易分享了从我们刚才上传进去的文件进行下载。需要掌握基本的开发流程,底层实现原理等。

一、文件上传原理

提供form表单,method必须是post

form表单的enctype必须是multipart/form-data

提供input type=”file”

Enctype属性

告知服务器请求正文的MIME类型。

55309ed0a8eb98a6539789a02a627c82.png

application/x-www-form-urlencoded(默认):

正文:name=aa&password=123

服务器获取数据:request.getParameter(“name”);

文件上传原理:

解析请求正文的每部分的内容。

基于html form表单上传的数据都是以类似—————————–7da3c8e180752{0x130x10}这样的分割符来标记一块数据的起止。

b09642a939cc361003c237edd3b030ba.png

文件上传的Content-Type为multipart/form-data; boundary=—-WebKitFormBoundaryhQslmBE7nbTLTJzD,而普通的form表单的Content-Type为application/x-www-form-urlencoded。因此,我们可以利用HttpServletRequest的request.getHeaderNames()方法和request.getHeaders(headName)方法得到请求头Headers中的Content-Type数据,然后根据Content-Type数据中是否包含multipart/form-data来区分请求是否为文件上传请求。其中boundary为文件数据的分隔符,用于区分上传多个文件。

二、使用第三方工具实现文件上传

fileupload组件工作流程:

a01af4042c852a0e47766207516aed62.png

开发步骤

导入commons-fileupload.jar、commons-io.jar包。

1、界面

我们就是需要一个form表单,为其添加enctype属性和post方法:

姓名:

照片:

2、逻辑处理

我们在一个servlet中进行处理:

request.setCharacterEncoding("UTF-8");

//判断用户的请求内容是不是multipart/form-data

boolean isMultipart=ServletFileUpload.isMultipartContent(request);

if(!isMultipart){

throw new RuntimeException("error!");

}

//创建DiskFileItemFactory对象

DiskFileItemFactory factory=new DiskFileItemFactory();

//创建核心解析类ServlertFileUpload

ServletFileUpload sfu=new ServletFileUpload(factory);

//解析请求对象

List items=new ArrayList(0);

try {

items=sfu.parseRequest(request);

} catch (FileUploadException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

for(FileItem item:items){

if(item.isFormField()){

processFormField(item);

}else{

processUploadField(item);

}

}

response.getWriter().write("sucess!");

接下来就是分为两种情况了,一种是对普通的表单元素进行处理,另一种是对文件类的数据进行处理,对于第一种情况的话就比较简单,我们直接获取名字就可以了,基本上不用过多的处理。

private void processFormField(FileItem item) {

String fieldName=item.getFieldName();

String fieldValue=item.getString();

System.out.println(fieldValue+"="+fieldName);

}

对于第二种情况,需要我们直接对上传文件进行一系列的处理了,我们首先需要在服务器上找一个存放文件的地方,然后截取上传的文件名、构建输出流、关闭流等操作。

private void processUploadField(FileItem item) {

try {

InputStream in=item.getInputStream();

String filename=item.getName();

//在服务器上找一个存放文件的地方

String storeDirectoryRealPath=getServletContext().getRealPath("/WEB-INF/files");

File storeDirectory=new File(storeDirectoryRealPath);

if(!storeDirectory.exists()){

storeDirectory.mkdirs();

}

//截取上传的文件名

//filename=filename.substring(filename.lastIndexOf(File.separator)+1);

if(filename!=null){

filename=FilenameUtils.getName(filename);

}

String guidFilename=GUIDUtil.generateGUID()+"_"+filename;

//按日期来区分存储目录

// String childDirectory=makeChileDirectory(storeDirectory);

String childDirectory=makeChildDirectory(storeDirectory,guidFilename);

//构建输出流

OutputStream out=new FileOutputStream(new File(storeDirectory,childDirectory+File.separator+guidFilename));

int len = -1;

byte buf[] = new byte[1024];

while((len=in.read(buf))!=-1){

out.write(buf, 0, len);

}

in.close();

out.close();

} catch (IOException e) {

e.printStackTrace();

}

}

三、文件上传优化处理

1、把保存的文件放在用户无法直接访问到的地方:例如放在:在WEB-INF/files目录中。

String storeDirectoryRealPath=getServletContext().getRealPath("/WEB-INF/files");

2、让文件名唯一。

String guidFilename=GUIDUtil.generateGUID()+"_"+filename;

//构建输出流

OutputStream out=new FileOutputStream(new File(storeDirectory,guidFilename));

3、避免同一个文件夹中的文件过多。

3.1按照日期进行存储。

String childDirectory=makeChileDirectory(storeDirectory);

private String makeChileDirectory(File storeDirectory) {

Date now=new Date();

DateFormat df=new SimpleDateFormat("yyyy-MM-dd");

String sdate=df.format(now);

File f=new File(storeDirectory,sdate);

if(!f.exists()){

f.mkdirs();

}

return sdate;

}

3.2用文件名的hashCode计算需要进行存储的目录,二级目录。

private String makeChildDirectory(File storeDirectory, String guidFilename) {

int hashCode = guidFilename.hashCode();

int dir1 = hashCode&0xf;// 0~15

int dir2 = (hashCode&0xf0)>>4;//0~15

String s = dir1+File.separator+dir2;

File f = new File(storeDirectory,s);

if(!f.exists()){

f.mkdirs();

}

return s;

}

4、限制文件的大小。web方式不适合上传大的文件。

4.1单个文件大小:

ServletFileUpload sfu=new ServletFileUpload(factory);

sfu.setFileSizeMax(4*1024*1024);//限制不超过4M

4.2总文件大小:多文件上传

ServletFileUpload sfu=new ServletFileUpload(factory);

sfu.setSizeMax(8*1024*1024);//总文件大小

5、限制文件的上传类型。

5.1通过文件扩展名来进行限制。

String extensionName=FilenameUtils.getExtension(filename);

5.2通过文件MIME类型来限制。

String mimeType=item.getContentType();

6、空文件上传解决方案。

判断文件名是否为空,当文件名为空时return。

7、临时文件

DiskFileItemFactory的作用是产生FileItem对象。其内部有一个缓存,默认大写拾10kb,如果上传文件超过10kb,则用磁盘作为缓存。存放缓存的目录默认是系统的临时目录。

DiskFileItemFactory factory=new DiskFileItemFactory();

//更改临时文件的存放目录

factory.setRepository(new File("D:/"));

如果是自己用IO流实现的文件上传,则需要在流关闭后,清理临时文件。

FileItem.delete();

可以使用FileItem.write(File f)实现文件上传的保存。

8、中文编码

request.setCharacterEncoding("UTF-8");

//该编码要和jsp页面保持一致

String fieldValue=item.getString("UTF-8");

9、动态js控制上传框

name:

photo:

var d1 = document.getElementById("d1");

var oldInnerHtml = d1.innerHTML;

d1.innerHTML=oldInnerHtml+"

photo:
";

}

function deleteOne(delBtn){

delBtn.parentNode.parentNode.removeChild(delBtn.parentNode);

}

00af4c48d05e1323ef42dc2cc438791c.png

四、文件的下载

首先我们来看一下页面的处理:

新建一个list.jsp文件:

全部资源如下:

${me.value}  下载

要记得引入jstl的核心包 。

接下做界面显示的servlet。

我们之前上传的文件是用GUID做过相应处理的,是一个拼接好的文件名,用来防止文件同名的情况发生,这里用户浏览到的文件当然要和上传的时候的文件名 相同,所以这里我们要进行截取,把GUID拼接的前缀去掉,以“_”分开。一个是在服务器中防同名的文件名以及显示出来的截取后的文件名。这里以前面说过的,防同名文件方法2:用文件名的hashCode计算需要进行存储的目录,二级目录。这里也就需要使用hashCode找到做过文件的路径。

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

//key:GUID文件名 value:old文件名

Map map = new HashMap();

//获取/WEB-INF/files的真实路径

String rootDirectoryRealPath = getServletContext().getRealPath("/WEB-INF/files");

//递归遍历找出所有的文件

System.out.println(rootDirectoryRealPath);

File rootDirectory = new File(rootDirectoryRealPath);

treeWalk(rootDirectory,map);

//存到请求范围中,转发给jsp显示

request.setAttribute("map", map);

request.getRequestDispatcher("/list.jsp").forward(request, response);

}

//递归遍历找出所有的文件,把文件名高出来

public void treeWalk(File file, Map map) {

if(file.isFile()){

String guidFileName = file.getName();

String oldFileName = guidFileName.substring(guidFileName.indexOf("_")+1);

map.put(guidFileName, oldFileName);

}else{

//目录

File[]childFiles = file.listFiles();

for(File f:childFiles){

treeWalk(f, map);

}

}

}

接下来就是下载的处理了。

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

String guidFilename = request.getParameter("filename");//get方式提交的

guidFilename = new String(guidFilename.getBytes("ISO-8859-1"),"UTF-8");

//计算存放路径

File storeDirectory = new File(getServletContext().getRealPath("/WEB-INF/files"));

String childDirectory = makeChildDirecotry(storeDirectory, guidFilename);// 13/1

//构建输入流

InputStream in = new FileInputStream(new File(storeDirectory,childDirectory+File.separator+guidFilename));

//用响应对象的输出流输出:下载的方式

String oldFileName = guidFilename.substring(guidFilename.indexOf("_")+1);

response.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode(oldFileName,"UTF-8"));//不适用火狐

response.setContentType("application/octet-stream");

OutputStream out = response.getOutputStream();

int len = -1;

byte buf[] = new byte[1024];

while((len=in.read(buf))!=-1){

out.write(buf, 0, len);

}

in.close();

}

private String makeChildDirecotry(File storeDirectory, String guidFilename) {

int hashCode = guidFilename.hashCode();

int dir1 = hashCode&0xf;

int dir2 = (hashCode&0xf0)>>4;

String s = dir1+File.separator+dir2;

File f = new File(storeDirectory,s);

if(!f.exists()){

f.mkdirs();

}

return s;

}

通过这个网址进行访问

abcd6aba2c86f8f255c1dd834d65e5fc.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值