Java学习笔记——javaweb 之文件的上传与下载、 windows环境下工程的打包发布

1、文件的上传介绍(*****重要)

  • 文件的上传主要分成两个步骤
    1. 在含有<input type=”file” >的表单页面中选择文件,然后将请求提交到Servlet
    2. Servlet收到请求,解析用户上传的文件,然后将文件存储到服务器

 

文件上传的时候我们需要使用 <input type="file" /> 这样的一个表单项。

并且还必须修改<form method="post" enctype="multipart/form-data">

文件上传的时候,method必须为post,并且enctype必须为multipart/form-data

 

当enctype=application/x-www-form-urlencoded 的时候,表示,浏览器要把所有的表单项,以name=value&name=value的形式进行拼接。

然后做url的编码操作,再发送到服务器

 

enctype=multipart/form-data 的时候,表示浏览器会把表单提交的数据,以二进制流的形式提交到服务器。

并且会以多段的形式来组装二进制流数据。

 

 

1.1、浏览器端的编解码操作。

      <script type="text/javascript">

         var url = "username=中文&password=123456";

         // 这叫编码操作。由浏览器完成!!!

         var encodedUrl = encodeURI(url);

         alert(encodedUrl);

         // 把编码后的url解码操作,还原成原来的地址。

         var decodeURL = decodeURI(encodedUrl);

         alert(decodeURL);

      </script>

 

 

1.2、Java中的编解码操作。

    public static void main(String[] args) throws UnsupportedEncodingException {

       

        String url = "username=中文&password=123456";

        // 把地址进行编码操作

        String encodeURL = URLEncoder.encode(url, "UTF-8");

        System.out.println(encodeURL);

        // 把地址进行解码操作。

        String decodeURL = URLDecoder.decode(encodeURL, "UTF-8");

        System.out.println(decodeURL);

    }

1.3、文件上传,HTTP协议的说明。

Java学习笔记——javaweb 之文件的上传与下载、 windows环境下工程的打包发布

要解析上面协议的内容。我们自己解析是很困难的。那我们怎么办呢。我们需要使用Apache组织给我们提供的一个叫做commons-fileupload-1.2.1.jar的jar包来解析。fileupload包需要依赖一个commons-io-1.4.jar。所以我们只需要使用这两个jar包就可以解析得到我们想要的内容。具体如何使用,请看下面的java代码。 下载地址:https://download.csdn.net/download/qq_25106373/10577746

 

1.4、commons-fileupload.jar 常用API介绍说明

 

boolean ServletFileUpload.isMultipartContent(HttpServletRequest request);

判断请求是否是文件上传的 multipart/form-data 格式

 

public List<FileItem> parseRequest(HttpServletRequest request)

解析request对象,获取请求参数,返回的是一个List,List中保存的是一个FileItem对象,一个对象代表一个请求表单项。

 

boolean FileItem.isFormField()

判断当前表单项是否是普通表单项。如果是普通项。返回true,如果是文件上传项。返回false

 

String FileItem.getFieldName()

返回表单项的name属性值

 

String FileItem.getString()

返回表单项的值。

 

String FileItem.getName();

返回上传文件的文件名。

 

void FileItem.write( file );

把上传的文件输入到指定的file位置

 

FileItemFactory 我们使用的是DiskFileItemFactory

解析Request对象使用ServletFileUpload类实例的.parseRequest(HttpServletRequest request)方法

 

 

 

1.5、fileupload类库的使用:

// 1、先判断是否是上传的协议

// 2、创建一个文件工厂FileItemFactory

// 3、创建一个用来解析上传协议的类ServletFileUpload

// 4、解析requestHTTP协议数据,生成表单项 parseRequest(HttpServletRequest request)

// 5、遍历处理每一个表单项FileItem。判断每个表单项。是否为上传文件。而做不同的相应的处理。

 

 

 

2)html页面中文件上传的表单内容:

 

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

    <head>

        <meta http-equiv="pragma" content="no-cache" />

        <meta http-equiv="cache-control" content="no-cache" />

        <meta http-equiv="Expires" content="0" />

        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

        <title>Insert title here</title>

    </head>

    <body>

        表单上传:

        <form action="upLoad" method="post" enctype="multipart/form-data">

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

            头像上传:<input name="file" type="file" /><br/>

            <input type="submit" />

        </form>

    </body>

</html>

 

 

 

3)创建一个Servlet类名为:UpLoad来接收上传的内容。代码如下:

 

fileupload类库的使用步骤:

// 1、先判断是否是上传的协议

// 2、创建一个文件工厂FileItemFactory

// 3、创建一个用来解析上传协议的类ServletFileUpload

// 4、解析requestHTTP协议数据,生成表单项

// 5、遍历处理每一个表单项。判断每个表单项。是否为上传文件。而做不同的相应的处理。

 

 

package com.atguigu.servlet;

 

import java.io.File;

import java.io.IOException;

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.FileItemFactory;

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

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

 

public class UpLoad extends HttpServlet {

    private static final long serialVersionUID = 1L;

 

    public UpdateLoad() {

        super();

    }

 

    protected void doPost(HttpServletRequest request, HttpServletResponse response)

            throws ServletException, IOException {

 

        // 1、先判断是否是上传的协议

        boolean isMultipart = ServletFileUpload.isMultipartContent(request);

        if (isMultipart) {

            try {

                // 2、创建一个文件工厂FileItemFactory

                FileItemFactory fileItemFactory = new DiskFileItemFactory();

                // 3、创建一个用来解析上传协议的类ServletFileUpload

                ServletFileUpload fileUpload = new ServletFileUpload(fileItemFactory);

 

                // 4、解析request数据

                List<FileItem> fileItems = fileUpload.parseRequest(request);

                System.out.println("表单项个数:" + fileItems.size());

                // 5、遍历处理每一个表单项

                for (FileItem fileItem : fileItems) {

                    // 判断是否是普通的表单项,还是上传的文件项

                    if (fileItem.isFormField()) {

                        System.out.println(fileItem.isFormField());

                        // 获取表单项name的值

                        System.out.println(fileItem.getFieldName());

                        // 获取表单项值

                        System.out.println(fileItem.getString());

                    } else {

                        // 打印测试

                        System.out.println(fileItem.isFormField());

                        System.out.println(fileItem.getFieldName());

                        // 获取文件名

                        System.out.println(fileItem.getName());

                        //生成uuid名字

                        String uuid = UUID.randomUUID().toString().replaceAll("-","");

                        System.out.println(uuid);

                        // 保存到工程中upload目录下

                        File f = new File(getServletContext().getRealPath("/upload") + "/" + uuid + "_"

                                + fileItem.getName());

                        System.out.println("file 路径:" + f);

                        // 把文件写入到指定路径。

                        fileItem.write(f);

                    }

                }

            } catch (Exception e) {

                e.printStackTrace();

            }

        } else {

            System.out.println("不是合法的文件上传!!");

        }

    }

 

}

 

 

3)在web.xml中的配置:

 

  <servlet>

    <servlet-name>UpLoad</servlet-name>

    <servlet-class>com.atguigu.servlet.UpLoad</servlet-class>

  </servlet>

  <servlet-mapping>

    <servlet-name>UpLoad</servlet-name>

    <url-pattern>/upLoad</url-pattern>

  </servlet-mapping>

 

2、文件上传练习

需求:在前面图像上传功能的基础之上。上传成功之后。跳转到一个页面把所有上传的图片都展示出来。

 

 

1) 修改上面的UpdateLoad类,实现显示功能

 

package com.atguigu.servlet;

 

import java.io.File;

import java.io.IOException;

import java.util.List;

import java.util.Map;

import java.util.UUID;

import java.util.concurrent.ConcurrentHashMap;

 

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.FileItemFactory;

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

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

 

public class UpLoad extends HttpServlet {

    private static final long serialVersionUID = 1L;

    // 使用map结构代码数据库保存

    private Map<String, String> imgs = new ConcurrentHashMap<String, String>();

 

    public UpdateLoad() {

        super();

    }

 

    protected void doPost(HttpServletRequest request, HttpServletResponse response)

            throws ServletException, IOException {

        // 用户名

        String username = null;

        // 头像路径

        String imgPath = null;

 

        // 1、先判断是否是上传的协议

        boolean isMultipart = ServletFileUpload.isMultipartContent(request);

        if (isMultipart) {

            try {

                // 2、创建一个文件工厂FileItemFactory

                FileItemFactory fileItemFactory = new DiskFileItemFactory();

                // 3、创建一个用来解析上传协议的类ServletFileUpload

                ServletFileUpload fileUpload = new ServletFileUpload(fileItemFactory);

 

                // 4、解析request数据

                List<FileItem> fileItems = fileUpload.parseRequest(request);

                System.out.println("表单项个数:" + fileItems.size());

                // 5、遍历处理每一个表单项

                for (FileItem fileItem : fileItems) {

                    // 判断是否是普通的表单项,还是上传的文件项

                    if (fileItem.isFormField()) {

                        System.out.println(fileItem.isFormField());

                        // 获取表单项name的值

                        System.out.println(fileItem.getFieldName());

                        // 获取表单项值

                        username = fileItem.getString("UTF-8");

                        System.out.println(fileItem.getString("UTF-8"));

                    } else {

                        // 打印测试

                        System.out.println(fileItem.isFormField());

                        System.out.println(fileItem.getFieldName());

                        // 获取文件名

                        System.out.println(fileItem.getName());

                        // 生成uuid名字

                        String uuid = UUID.randomUUID().toString().replaceAll("-", "");

                        imgPath = request.getContextPath() + "/upload/" + uuid + "_"

                                + fileItem.getName();

                        System.out.println(imgPath);

                        // 保存到工程中upload目录下

                        File f = new File(getServletContext().getRealPath("/upload") + "/" + uuid

                                + "_" + fileItem.getName());

                        System.out.println("file 路径:" + f);

                        // 把文件写入到指定路径。

                        fileItem.write(f);

                    }

                }

                // 添加到map中

                imgs.put(username, imgPath);

                // 存放到request域对象中

                request.setAttribute("imgs", imgs);

                // 转发到展示页面

                request.getRequestDispatcher("showImgs.jsp").forward(request, response);

            } catch (Exception e) {

                e.printStackTrace();

            }

        } else {

            System.out.println("不是合法的文件上传!!");

        }

 

    }

 

}

 

 

2) 添加一个showImg.jsp页面显示所有上传的图片

 

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

    pageEncoding="UTF-8"%>

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

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

    <head>

        <meta http-equiv="pragma" content="no-cache" />

        <meta http-equiv="cache-control" content="no-cache" />

        <meta http-equiv="Expires" content="0" />

        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

        <title>Insert title here</title>

    </head>

    <body>

        <%-- 遍历图片 --%>

        <c:forEach items="${ imgs }" var="item">

            <div style="width: 120px; float: left; border: 1px red solid; padding: 5px;">

                <span>${ item.key }</span>

                <img width="100" height="120" src="${ item.value }" />

            </div>

        </c:forEach>

    </body>

</html>

 

3、文件下载

  1. 文件的下载需要把文件以二进制流的形式返回给客户端。并且告诉客户端返回的数据的类型。

获取客户端的输出流:response.getOutputStream();

获取下载文件的输入流:servletContext.getResourceAsStream();

设置响应的数据类型 response.setContentType();

获取文件的数据类型 servletContext.getMimeType();

 

1、下载的步骤:

  1. 读取下载的文件,获取到输入流对象
  2. 通过response对象获取到输出流
  3. 把下载的数据,通过输出流。输出到客户端。
  4. 输出之前我们要告诉浏览器输出的数据的类型response.setContentType();

 

2、但是通过前面的操作,我们不难发现。有些像图片,文本的数据,浏览器会直接显示出来。而我们的目的是想让浏览器进行下载操作。

所以这个时候需要设置响应头

response.setHeader("Content-Disposition", "attachment; fileName=1.jpg");

这个响应头告诉浏览器。这是需要下载的。而attachment表示附件,也就是下载的一个文件。fileName=后面,表示下载的文件名。

 

 

3、完成上面的两个步骤,下载文件是没问题了。但是如果我们要下载的文件是中文名的话。你会发现,下载无法正确显示出正确的中文名。

原因是在响应头中,不能包含有中文字符,只能包含ASCII码。

 

附件中文名乱码问题解决方案:

 

 

方案一:URLEncoder解决 IE 和谷歌浏览器的  附件中文名问题。

如果客户端浏览器是IE浏览器 或者 是谷歌浏览器。我们需要使用URLEncoder类先对中文名进行UTF-8的编码操作。

因为IE浏览器和谷歌浏览器收到含有编码后的字符串后会以UTF-8字符集进行解码显示。

 

            // 把中文名进行UTF-8编码操作。

            String str = "attachment; fileName=" + URLEncoder.encode("中文.jpg", "UTF-8");

            // 然后把编码后的字符串设置到响应头中

            response.setHeader("Content-Disposition", str);

 

 

BASE64编解码的说明

 

如果客户端浏览器是火狐浏览器。 那么我们需要对中文名进行BASE64的编码操作。

 

这时候需要把请求头Content-Disposition: attachment; filename=中文名

编码成为:Content-Disposition: attachment; filename==?charset?B?xxxxx?=

 

=?charset?B?xxxxx?= 现在我们对这段内容进行一下说明。

=?         表示这段编码内容开始的符号

charset    表示编码的字符集

B           表示使用Base64进行编码操作。

xxxx       表示 filename原来的值进行Base64编码后的内容。

?=         表示整段编码内容的结束标记。

 

BASE64编解码使用示例:

    public static void main(String[] args) throws Exception {

 

        String string = "中文";

 

        // 生成BASE64的编码器

        BASE64Encoder encoder = new BASE64Encoder();

        // 编码操作

        String encodeString = encoder.encode(string.getBytes("UTF-8"));

 

        System.out.println(encodeString);

        // 生成一个BASE64的解码器

        BASE64Decoder decoder = new BASE64Decoder();

        // 解码为byte数组

        byte[] buffer = decoder.decodeBuffer(encodeString);

        // 将字节数组还原为原来的中文

        String string2 = new String(buffer, "UTF-8");

        System.out.println(string2);

 

    }

 

方案二:BASE64编解码 解决 火狐浏览器的附件中文名问题

因为火狐使用的是BASE64的编解码方式还原响应中的汉字。所以需要使用BASE64Encoder类进行编码操作。

 

 

 

            // 使用下面的格式进行BASE64编码后

            String str = "attachment; fileName=" + "=?utf-8?B?"

                    + new BASE64Encoder().encode("中文.jpg".getBytes("utf-8")) + "?=";

            // 设置到响应头中

            response.setHeader("Content-Disposition", str);

 

 

那么我们如何解决上面两种不同编解码方式呢。我们只需要通过判断请求头中User-Agent这个请求头携带过来的浏览器信息即可判断出是什么浏览器。

 

如下:

        String ua = request.getHeader("User-Agent");

        // 判断是否是火狐浏览器

        if (ua.contains("Firefox")) {

            // 使用下面的格式进行BASE64编码后

            String str = "attachment; fileName=" + "=?utf-8?B?"

                    + new BASE64Encoder().encode("中文.jpg".getBytes("utf-8")) + "?=";

            // 设置到响应头中

            response.setHeader("Content-Disposition", str);

        } else {

            // 把中文名进行UTF-8编码操作。

            String str = "attachment; fileName=" + URLEncoder.encode("中文.jpg", "UTF-8");

            // 然后把编码后的字符串设置到响应头中

            response.setHeader("Content-Disposition", str);

        }

 

 

 

 

方案三:使用 GBK编码,ISO-8859-1解码方式解决  附件中文名问题(非标准方案)

不管是任何的浏览器。只需要把中文名通过gbk字符集方式进行编码。然后再以iso-8859-1字符集进行解码。然后于设置到响应头中。经实验。可以在任何浏览器中成功解决中文名的问题。

 

        // 解决下载文件名中文乱码,

        // 此方法无解,但是管用

         String str = "attachment; fileName=" + new String("中文.jpg".getBytes("gbk"), "iso-8859-1");

         response.setHeader("Content-Disposition", str);

 

 

 

 

package com.atguigu.servlet;

 

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.URLEncoder;

 

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

import org.apache.commons.io.IOUtils;

 

import sun.misc.BASE64Encoder;

 

/**

 * Servlet implementation class Download

 */

public class Download extends HttpServlet {

    private static final long serialVersionUID = 1L;

 

    public Download() {

        super();

    }

 

    protected void doGet(HttpServletRequest request, HttpServletResponse response)

            throws ServletException, IOException {

        // 读取要下载的文件

        InputStream is = getServletContext().getResourceAsStream("/upload/2.jpg");

        // 获取输出流

        OutputStream os = response.getOutputStream();

        // 获取文件类型// 这里请换成你自己的文件名去下载

        String mimeType = getServletContext().getMimeType("/upload/2.jpg");

        // 告诉浏览器。返回的内容类型。为下载

        response.setContentType(mimeType);

        // 解决下载文件名中文乱码,方法一

        // 通过判断不同浏览器,做不同的处理

        String ua = request.getHeader("User-Agent");

        if (ua.contains("Firefox")) {

            String str = "attachment; fileName=" + "=?utf-8?B?"

                    + new BASE64Encoder().encode("中文.jpg".getBytes("utf-8")) + "?=";

            response.setHeader("Content-Disposition", str);

        } else {

            String str = "attachment; fileName=" + URLEncoder.encode("中文.jpg", "UTF-8");

            response.setHeader("Content-Disposition", str);

        }

 

        // 解决下载文件名中文乱码,方法二

        // 此方法无解,但是管用

        // String str = "attachment; fileName=" + new String("中文.jpg".getBytes("gbk"), "iso-8859-1");

        // response.setHeader("Content-Disposition", str);

 

        // 从输入流读取数据,输出到输出流

        IOUtils.copy(is, os);

        is.close();

        os.close();

    }

}

 

4、打包布置

首先,我们大多数时候布署的服务器,通常是linux系统。这个我们之后再谈。今天我们来看看。如何在window下布署工程。

前面我们讲过。在Tomcat中布署工程,只需要把工程布署到webapps目录下即可。那我们的web工程。发布署之前。还要做一件事情,那就是打包为war文件。

 

4.1、如何将web工程打包成为war文件。

1)选中你要发布的web工程。右键,选Expor菜单 。t

Java学习笔记——javaweb 之文件的上传与下载、 windows环境下工程的打包发布

 

2)过滤找到我们要导出的war。点击Next>下一步

Java学习笔记——javaweb 之文件的上传与下载、 windows环境下工程的打包发布

 

3)选择导出的配置

 

Java学习笔记——javaweb 之文件的上传与下载、 windows环境下工程的打包发布

 

最后得到的是这样的一个文件:

 

Java学习笔记——javaweb 之文件的上传与下载、 windows环境下工程的打包发布

 

然后我们可以把上面这个book.war文件拷贝到webapps目录下,启动Tomcat即可访问book工程。

 

4.2、将Tomcat 安装成window服务

我们如果要使用window系统来做服务器。那么Tomcat服务器就必须要开机启动。安装成为window服务进程。安装的方法如上图。

 

1)先cd 到Tomcat的bin目录下

Java学习笔记——javaweb 之文件的上传与下载、 windows环境下工程的打包发布

 

2)然后执行service.bat install 命令

Java学习笔记——javaweb 之文件的上传与下载、 windows环境下工程的打包发布

 

3)然后到服务管理界面,我们发现。Apache Tomcat 6 服务器 已经被安装到系统中

Java学习笔记——javaweb 之文件的上传与下载、 windows环境下工程的打包发布

 

 

4)移除安装的服务 执行service.bat remove 命令。

Java学习笔记——javaweb 之文件的上传与下载、 windows环境下工程的打包发布