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

文章出处: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类型。



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

正文:name=aa&password=123

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

文件上传原理:

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

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

文件上传的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组件工作流程:

开发步骤

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

1、界面

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

     <form action="${pageContext.request.contextPath}/servlet/UploadServlet2"  method="post"  enctype="multipart/form-data" >
姓名: <input type="text" name="name" /><br />
照片: <input type="file" name="photo" /><br /> <input type="submit" value="提交" />
</form>

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<FileItem> items=new ArrayList<FileItem>(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控制上传框

   <form action="${pageContext.request.contextPath}/servlet/UploadServlet3" method="post" enctype="multipart/form-data">
name:<input type="text" name="name"/><br/>
<div id="d1">
<div>
photo:<input type="file" name="photo"/><input type="button" value="继续上传" onclick="addFile()"/>
</div>
</div>
<input type="submit" value="上传"/>
</form>
<script type="text/javascript">
function addFile(){
var d1 = document.getElementById("d1");
var oldInnerHtml = d1.innerHTML;
d1.innerHTML=oldInnerHtml+"<div>photo:<input type='file' name='photo'/><input type='button' value='删除' onclick='deleteOne(this)'/></div>";
}
function deleteOne(delBtn){
delBtn.parentNode.parentNode.removeChild(delBtn.parentNode);
}
</script>

四、文件的下载

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

新建一个list.jsp文件:

  全部资源如下:<br />

   <c:forEach items="${map}" var="me">
<c:url value="/servlet/DownLoadServlet" var="url">
<c:param name="filename" value="${me.key}"></c:param>
</c:url>
${me.value}&nbsp;&nbsp;<a href="${url}">下载</a><br/>
</c:forEach>

要记得引入jstl的核心包 。

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

接下做界面显示的servlet。

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

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//key:GUID文件名 value:old文件名
Map<String, String> map = new HashMap<String, String>();
//获取/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<String, String> 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;
}

通过这个网址进行访问

http://localhost:8080/UploadDemo/servlet/ShowAllFilesServlet

源码下载:https://github.com/sdksdk0/UploadDemo

Java web文件上传下载的更多相关文章

  1. java web 文件上传下载

    文件上传下载案例: 首先是此案例工程的目录结构:

  2. Java Web文件上传

    参考资料:http://www.cnblogs.com/xdp-gacl/p/4200090.html 一.问题描述 Java Web文件上传需要借助一些第三方库,常用的是借助Apache的包,有两个 ...

  3. WEB文件上传下载功能

    WEB文件上传下载在日常工作中经常用到的功能 这里用到JS库 http://files.cnblogs.com/meilibao/ajaxupload.3.5.js 上传代码段(HTML) <% ...

  4. CentOS下安装配置NFS并通过Java进行文件上传下载

    1:安装NFS (1)安装 yum install nfs-utils rpcbind (2)启动rpcbind服务 systemctl restart rpcbind.service 查看服务状态 ...

  5. java实现文件上传下载

    喜欢的朋友可以关注下,粉丝也缺. 今天发现已经有很久没有给大家分享一篇技术文章了,于是想了一下给大家分享一篇java实现文件上传下载功能的文章,不喜欢的希望大家勿喷. 想必大家都知道文件的上传前端页面 ...

  6. Java Web文件上传原理分析(不借助开源fileupload上传jar包)

    Java Web文件上传原理分析(不借助开源fileupload上传jar包) 博客分类: Java Web   最近在面试IBM时,面试官突然问到:如果让你自己实现一个文件上传,你的代码要如何写,不 ...

  7. java web service 上传下载文件

    1.新建动态web工程youmeFileServer,新建包com,里面新建类FileProgress package com; import java.io.FileInputStream; imp ...

  8. SpringMVC整合fastdfs-client-java实现web文件上传下载

    原文:http://blog.csdn.net/wlwlwlwl015/article/details/52682153 本篇blog主要记录一下SpringMVC整合FastDFS的Java客户端实 ...

  9. java+大文件上传下载

    文件上传下载,与传统的方式不同,这里能够上传和下载10G以上的文件.而且支持断点续传. 通常情况下,我们在网站上面下载的时候都是单个文件下载,但是在实际的业务场景中,我们经常会遇到客户需要批量下载的场 ...

随机推荐

  1. 1.UTF8字符集csv文件在oracle下乱码问题处理

    1.问题描述 在excel中生成了一个UTF-8编码格式的csv文件准备导入数据库,在notpad++下打开显示正常,编码集为UTF-8,通过pl/sql dev导入oracle是出现乱码,此时初步推 ...

  2. 浅谈linux静态库、动态库。

    动态库又叫动态共享文件(.so,Dynamic Shared Objects)和静态库(.a)都是将一些待重用的公共代码打包成一种特殊的重定位目标文件. 在使用时,连接器会将静态库中所有的代码,编译到 ...

  3. [CEOI 2004]Sweet

    Description 题面链接 有 \(n\) 种糖果.第 \(i\) 种糖果有 \(m_i\) 个.取出一些糖果,至少 \(a\) 个,但不超过 \(b\) 个.求方案数. \(1\leq n\l ...

  4. [NOIp 2009]Hankson的趣味题

    Description Hanks 博士是 BT (Bio-Tech,生物技术) 领域的知名专家,他的儿子名叫 Hankson.现在,刚刚放学回家的 Hankson 正在思考一个有趣的问题. 今天在课 ...

  5. 【BZOJ1040】【ZJOI2008】骑士

    Description Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬. 最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争.战 ...

  6. ●洛谷P1291 [SHOI2002]百事世界杯之旅

    题链: https://www.luogu.org/recordnew/show/5861351题解: dp,期望 定义dp[i]表示还剩下i个盖子没收集时,期望还需要多少次才能手机完. 初始值:dp ...

  7. 计蒜客NOIP2017提高组模拟赛(三)day2-小区划分

    传送门 dp,注意边界 #include<cstdio> #include<cstdlib> #include<algorithm> #include<cst ...

  8. HDU 5506(GT and set)

    题意: 表示看了很久,然而发现还是没看懂题. 正解:给你a个集合,让你把他们合并成k个,当两个集合有公共数字时可以合并. (一直以为是合并后,每个集合至少有两个数字相同- -,这英语也是醉了) 思路: ...

  9. [Apio2009][bzoj1179]Atm

    题意:一个n个点m条单向边的图,每个点有权值,给定出发点和p个可以停止的点,你可以随便走一条路径从出发点走到一个可以停止的点,但是每个点的点权只能计算一次,求能得到的最大权值. n,m<=500 ...

  10. PHP 扩展开发检测清单(扩展开发必读)

    想要做出一个成功的 PHP 扩展包,不仅仅是简单的将代码放进文件夹中就可以了,除此之外,还有非常多的因素来决定你的扩展是否优秀.以下清单的内容将有助于完善你的扩展,并且在 PHP 社区中得到更多的重视 ...