在Web应用系统开发中,文件上传和下载功能是非常常用的功能。

对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,如果直接使用Servlet获取上传文件的输入流然后再解析里面的请求参数是比较麻烦,所以一般选择采用apache的开源工具common-fileupload这个文件上传组件。这个common-fileupload上传组件的jar包可以去apache官网上面下载。common-fileupload是依赖于common-io这个包的,所以还需要下载这个包。

开发环境搭建

创建一个FileUploadAndDownload项目,加入Apache的commons-fileupload文件上传组件的相关Jar包,如图20所示。

图20  导入Jar包

实现文件上传

●  编写文件上传页面,upload.jsp页面代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"%>

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>兄弟连IT教育</title>

</head>

<body>

<form action="${pageContext.request.contextPath}/upload"

enctype="multipart/form-data" method="post">

上传用户:<input type="text" name="username"><br />

上传文件1:<input type="file" name="file1"><br />

上传文件2:<input type="file" name="file2"><br />

<input type="submit" value="提交">

</form>

</body>

</html>

●  编写消息提示页面,message.jsp页面代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"%>

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>兄弟连IT教育</title>

</head>

<body>

${message}

</body>

</html>

●  编写处理文件上传的Servlet

package com.xdl.servlet;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.util.List;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;

import org.apache.commons.fileupload.disk.DiskFileItemFactory;

import org.apache.commons.fileupload.servlet.ServletFileUpload;

public class UploadServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

public void service(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException {

// 得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,

// 不允许外界直接访问,保证上传文件的安全

String savePath = this.getServletContext().

getRealPath("/WEB-INF/ upload");

File file = new File(savePath);

// 判断上传文件的保存目录是否存在

if (!file.exists() && !file.isDirectory()) {

System.out.println(savePath + "目录不存在,需要创建");

// 创建目录

file.mkdir();

}

// 消息提示

String message = "";

try {

// 使用Apache文件上传组件处理文件上传步骤:

// 1、创建一个DiskFileItemFactory工厂

DiskFileItemFactory factory = new DiskFileItemFactory();

// 2、创建一个文件上传解析器

ServletFileUpload upload = new ServletFileUpload(factory);

// 解决上传文件名的中文乱码

upload.setHeaderEncoding("UTF-8");

// 3、判断提交上来的数据是否是上传表单的数据

if (!ServletFileUpload.isMultipartContent(request)) {

// 按照传统方式获取数据

return;

}

// 4、使用ServletFileUpload解析器解析上传数据,

//解析结果返回的是一个List<FileItem>集合,

//每一个FileItem对应一个Form表单的输入项

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

for (FileItem item : list) {

// 如果fileitem中封装的是普通输入项的数据

if (item.isFormField()) {

String name = item.getFieldName();

// 解决普通输入项的数据的中文乱码问题

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

System.out.println(name + "=" + value);

} else {// 如果fileitem中封装的是上传文件

// 得到上传的文件名称,

String filename = item.getName();

System.out.println(filename);

if (filename == null || filename.trim().equals("")) {

continue;

}

// 处理获取到的上传文件的文件名的路径部分,只保留文件名部分

filename = filename.substring

(filename.lastIndexOf ("\\") + 1);

// 获取item中的上传文件的输入流

InputStream in = item.getInputStream();

// 创建一个文件输出流

FileOutputStream out =

new FileOutputStream(savePath + "\\" + filename);

// 创建一个缓冲区

byte buffer[] = new byte[1024];

// 判断输入流中的数据是否已经读完的标识

int len = 0;

// 循环将输入流读入到缓冲区当中,

(len=in.read(buffer))>0就表示in里面还有数据

while ((len = in.read(buffer)) > 0) {

out.write(buffer, 0, len);

}

// 关闭输入流

in.close();

// 关闭输出流

out.close();

// 删除处理文件上传时生成的临时文件

item.delete();

message = "文件上传成功!";

}

}

} catch (Exception e) {

message = "文件上传失败!";

e.printStackTrace();

}

request.setAttribute("message", message);

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

}

}

●  在web.xml文件中注册UploadServlet。

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns="http://xmlns.jcp.org/xml/ns/javaee"

xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"

version="3.1">

<servlet>

<servlet-name>UploadServlet</servlet-name>

<servlet-class>com.xdl.servlet.UploadServlet</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>UploadServlet</servlet-name>

<url-pattern>/upload</url-pattern>

</servlet-mapping>

</web-app>

启动Tomcat服务器,运行效果如图21、图22、图23和图24所示。

图21  上传文件

图22  上传文件成功

图23  控制台中打印了上传文件的文件名

图24  服务器端接收到了客户端上传的文件

文件上传的细节

上述的代码虽然可以成功将文件上传到服务器上面的指定目录当中,但是文件上传功能有许多需要注意的小细节问题,以下列出的几点需要特别注意的

●  为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于WEB-INF目录下。

●  为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名。

●  为防止一个目录下面出现太多文件,要使用hash算法打散存储。

●  要限制上传文件的最大值。

●  要限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。

●  针对上述提出的5点细节问题,我们来改进一下UploadServlet,改进后的代码如下:

package com.xdl.servlet;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.util.List;

import java.util.UUID;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;

import org.apache.commons.fileupload.FileUploadBase;

import org.apache.commons.fileupload.ProgressListener;

import org.apache.commons.fileupload.disk.DiskFileItemFactory;

import org.apache.commons.fileupload.servlet.ServletFileUpload;

public class UploadServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

public void service(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException {

// 得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,

// 不允许外界直接访问,保证上传文件的安全

String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");

// 上传时生成的临时文件保存目录

String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp");

File tmpFile = new File(tempPath);

if (!tmpFile.exists()) {

// 创建临时目录

tmpFile.mkdir();

}

// 消息提示

String message = "";

try {

// 使用Apache文件上传组件处理文件上传步骤:

// 1、创建一个DiskFileItemFactory工厂

DiskFileItemFactory factory = new DiskFileItemFactory();

// 设置工厂的缓冲区的大小,当上传的文件大小超过缓冲区的大小时,

//就会生成一个临时文件存放到指定的临时目录当中。

factory.setSizeThreshold(1024 * 100);

// 设置缓冲区的大小为100KB,如果不指定,那么缓冲区的大小默认是10KB

// 设置上传时生成的临时文件的保存目录

factory.setRepository(tmpFile);

// 2、创建一个文件上传解析器

ServletFileUpload upload = new ServletFileUpload(factory);

// 监听文件上传进度

upload.setProgressListener(new ProgressListener() {

public void update(long pBytesRead, long pContentLength,

int arg2) {

System.out.println("文件大小为:" + pContentLength

+ ",当前已处理:" + pBytesRead);

}

});

// 解决上传文件名的中文乱码

upload.setHeaderEncoding("UTF-8");

// 3、判断提交上来的数据是否是上传表单的数据

if (!ServletFileUpload.isMultipartContent(request)) {

// 按照传统方式获取数据

return;

}

// 设置上传单个文件的大小的最大值,目前是设置为1024*1024字节,也就是1MB

upload.setFileSizeMax(1024 * 1024);

// 设置上传文件总量的最大值

upload.setSizeMax(1024 * 1024 * 10);

// 4、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个

// List<FileItem>集合,每一个FileItem对应一个Form表单的输入项

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

for (FileItem item : list) {

// 如果fileitem中封装的是普通输入项的数据

if (item.isFormField()) {

String name = item.getFieldName();

// 解决普通输入项的数据的中文乱码问题

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

System.out.println(name + "=" + value);

} else {// 如果fileitem中封装的是上传文件

// 得到上传的文件名称

String filename = item.getName();

System.out.println(filename);

if (filename == null || filename.trim().equals("")) {

continue;

}

// 处理获取到的上传文件的文件名的路径部分,只保留文件名部分

filename = filename.substring(

filename.lastIndexOf("\\") + 1);

// 得到上传文件的扩展名

String fileExtName =

filename.substring(filename.

lastIndexOf(".") + 1);

// 如果需要限制上传的文件类型,那么可以通过文件的扩展名来判断上

// 传的文件类型是否合法

System.out.println("上传的文件的扩展名是:" + fileExtName);

// 获取item中的上传文件的输入流

InputStream in = item.getInputStream();

// 得到文件保存的名称

String saveFilename = makeFileName(filename);

// 得到文件的保存目录

String realSavePath = makePath(saveFilename, savePath);

// 创建一个文件输出流

FileOutputStream out = new FileOutputStream

(realSavePath + "\\" + saveFilename);

// 创建一个缓冲区

byte buffer[] = new byte[1024];

// 判断输入流中的数据是否已经读完的标识

int len = 0;

// 循环将输入流读入到缓冲区当中

while ((len = in.read(buffer)) > 0) {

out.write(buffer, 0, len);

}

// 关闭输入流

in.close();

// 关闭输出流

out.close();

// 删除处理文件上传时生成的临时文件

// item.delete();

message = "文件上传成功!";

}

}

} catch (FileUploadBase.FileSizeLimitExceededException e) {

e.printStackTrace();

request.setAttribute("message", "单个文件超出最大值!!!");

request.getRequestDispatcher("/message.jsp").

forward(request, response);

return;

} catch (FileUploadBase.SizeLimitExceededException e) {

e.printStackTrace();

request.setAttribute("message",

"上传文件的总的大小超出限制的最大值!!!");

request.getRequestDispatcher("/message.jsp").

forward(request, response);

return;

} catch (Exception e) {

message = "文件上传失败!";

e.printStackTrace();

}

request.setAttribute("message", message);

request.getRequestDispatcher("/message.jsp").

forward(request, response);

}

private String makeFileName(String filename) {

// 为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名

return UUID.randomUUID().toString() + "_" + filename;

}

/**

* 为防止一个目录下面出现太多文件,要使用hash算法打散存储

*/

private String makePath(String filename, String savePath) {

// 得到文件名的hashCode的值,得到的就是filename这个字符串对象在内存中的地址

int hashcode = filename.hashCode();

int dir1 = hashcode & 0xf;

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

// 构造新的保存目录

String dir = savePath + "\\" + dir1 + "\\" + dir2;

// File既可以代表文件也可以代表目录

File file = new File(dir);

// 如果目录不存在

if (!file.exists()) {

// 创建目录

file.mkdirs();

}

return dir;

}

}

针对上述提出的5点小细节问题进行改进之后,我们的文件上传功能就算是做得比较完善了。

IT兄弟连 JavaWeb教程 文件上传技术的更多相关文章

  1. JavaWeb:实现文件上传

    JavaWeb:实现文件上传 理解文件上传: 1.上传文件就是把客户端的文件发送给服务器端. 2.HTTP响应的正文部分最常见的是HTML文档,但是也可以是其他任意格式的数据,如图片和声音文件中的数据 ...

  2. JavaWeb实现文件上传下载功能实例解析

    转:http://www.cnblogs.com/xdp-gacl/p/4200090.html JavaWeb实现文件上传下载功能实例解析 在Web应用系统开发中,文件上传和下载功能是非常常用的功能 ...

  3. JavaWeb:实现文件上传与下载

    JavaWeb:实现文件上传与下载 文件上传前端处理 本模块使用到的前端Ajax库为Axio,其地址为GitHub官网. 关于文件上传 上传文件就是把客户端的文件发送给服务器端. 在常见情况(不包含文 ...

  4. JavaWeb实现文件上传下载功能实例解析 (好用)

    转: JavaWeb实现文件上传下载功能实例解析 转:http://www.cnblogs.com/xdp-gacl/p/4200090.html JavaWeb实现文件上传下载功能实例解析 在Web ...

  5. 使用传统javaweb进行文件上传

    使用传统文件上传方式 1.配置依赖 <properties> <project.build.sourceEncoding>UTF-8</project.build.sou ...

  6. Spring Boot 教程 - 文件上传下载

    在日常的开发工作中,基本上每个项目都会有各种文件的上传和下载,大多数文件都是excel文件,操作excel的JavaAPI我用的是apache的POI进行操作的,POI我之后会专门讲到.此次我们不讲如 ...

  7. JavaWeb多文件上传及zip打包下载

    项目中经常会使用到文件上传及下载的功能.本篇文章总结场景在JavaWeb环境下,多文件上传及批量打包下载功能,包括前台及后台部分.  首先明确一点:  无法通过页面的无刷新ajax请求,直接发下载.上 ...

  8. PHP高级教程-文件上传

    PHP 文件上传 通过 PHP,可以把文件上传到服务器. 本章节实例在 test 项目下完成,目录结构为: test |-----upload # 文件上传的目录 |-----form.html # ...

  9. 深入分析JavaWeb Item40 -- 文件上传和下载

    在Web应用系统开发中,文件上传和下载功能是很经常使用的功能,今天来讲一下JavaWeb中的文件上传和下载功能的实现. 对于文件上传.浏览器在上传的过程中是将文件以流的形式提交到server端的.假设 ...

随机推荐

  1. view上下抖动特效

    shake.xml <?xml version="1.0" encoding="utf-8"?> <translate xmlns:andro ...

  2. SqlServer 按逗号分隔

    SELECT ORDER_ID,LTRIM(MAX(SYS_CONNECT_BY_PATH(GOODS_NAME, ',')), ',') GOODS_NAME FROM (SELECT GOODS_ ...

  3. 去js校验

    1.首先,找到项目所在的路径 2.关掉eclipse,找到项目文件 3.打开.project

  4. zabbix 上 mysql 优化

    摘自: https://segmentfault.com/a/1190000001638101

  5. Android SDK和NDK

    NDK是用来给安卓手机开发软件用的,但是和SDK不同的是它用的是C语言,而SDK用的是Java语言.NDK开发的软件在安卓的环境里是直接运行的,一般只能在特定的CPU指令集的机器上运行,而且C语言可以 ...

  6. 为什么修改头文件make不重新编译

    make是根据依赖文件的时间戳来决定要不要重新编译的.在: object: deplist # actions 中,可以把头文件加进deplist,这样修改头文件后,make就会重新编译了. 单纯地修 ...

  7. Android Studio的技巧

    1.快速添加add unimplements methods:  右键generate 2.快速添加try-catch:左边就有一个小电灯,然后可以选. 3.格式化OPTION + CMD + L ( ...

  8. Spring笔记01(基础知识)

    1.基础知识 01.Spring:轻量级Java EE开源框架,它是由Rod Johnson为了解决企业应用程序开发的复杂性而创建. 02.目标:实现一个全方位的整合框架,实现“一站式”的企业应用开发 ...

  9. ACM学习历程—ZOJ 3861 Valid Pattern Lock(dfs)

    Description Pattern lock security is generally used in Android handsets instead of a password. The p ...

  10. 【bzoj1232】[Usaco2008Nov]安慰奶牛cheer

    问题描述 Farmer John变得非常懒,他不想再继续维护供奶牛之间供通行的道路.道路被用来连接N个牧场,牧场被连续地编号为1到N.每一个牧场都是一个奶牛的家.FJ计划除去P条道路中尽可能多的道路, ...