JavaWeb实现简单的文件上传和下载

一、文件上传
1.1 文件上传的简单实现
前端的标签介绍
① 需要一个表单标签,请求方式为post请求

PS:因为get请求时url有长度限制,而带有文件上传的url一般会超出get请求的长度限制,所以只能用post

② 标签中需添加enctype属性,属性值必须为multipart/form-data

enctype属性:encodetype的缩写,就是编码类型的意思
multipart/form-data属性值:multipart是多元的意思,表示数据由多段的形式拼接而成,既有文本又有文件,每一个表单项表示一段数据,拼接后以二进制流的方式提交到服务器
PS:enctype属性值只有设置为multipart/form-data时,才能实现文件的上传

③ 标签里面添加标签,在此处添加需要上传的文件
④ 后台服务器中使用Servlet 接受和处理上传的数据

实现代码

前端页面

<%-- 添加表单项 --%>
<form action="" method="post" enctype="multipart/form-data">
    用户名:<input type="text" name="username"> <br>
    头像:<input type="file" name="profile"> <br>
    <input type="submit" value="上传">
</form>

后台Servlet

@WebServlet("/fileUpload")
public class FileUploadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("文件上传成功!");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }
}

1.2 http协议内容分析
分析图
在这里插入图片描述

分析请求头
Content-Type参数:对应表单中的enctype属性,表示提交数据的编码类型
Content-Type的两个参数值
multipart/form-data:对应enctype属性值,就是表示提交的数据以多段形式拼接而成,并以二进制流形式发给服务器
boundary=分隔符:表示每段数据的分隔符,由浏览器每次随机生成,这里演示的分隔符为----WebKitFormBoundarynn2YwuHk30PBRDRM(前面一般带有4个减号)
分析请求体
----WebKitFormBoundarynn2YwuHk30PBRDRM:分隔符,表示一段数据(一个表单项)的开始
----WebKitFormBoundarynn2YwuHk30PBRDRM–:结束分隔符,在原来的分隔符后再添加2个减号,表示整段数据的结束,即在最后一个表单项后面出现
表单项内容分析
Content-Disposition:对当前表单项的描述,参数值有form-data、name=“表单名”
Content-Type:表示上传的文件MIME类型,值为image/jpeg
空行:在表单项描述和表单项值之间会有空行隔开,类似于请求头与请求体之间的请求空行
表单项的值:如果是文本类型的表单项,就直接显示表单项输入框中的值,如果是文件类型,就是一些文件数据,一般文件的数据都会很多,所以浏览器会隐藏起来,但可以在后台打印查看
1.3 解析文件上传时的数据
使用第三方(apache)jar包:commons-fileupload-…jar、commons-io-…jar

jar包中重要类及其方法介绍

ServeltFileUpload类:用于解析文件上传时的数据
boolean isMultipartContent(req):ServletFileUpload类中的静态方法,判断上传的表单数据是否是多段的格式
List parseRequest(req):解析上传的数据(多段数据格式的前提下)
FileItem类:对应一个表单项
boolean isFormField():判断当前的表单项是否是普通类型的表单项,true表示普通表单项,如文本,false表示非普通表单项,如文件
String getFieldName():获取表单项的名称,即获取标签的name属性值
String getString(“utf-8”):获取普通表单项的值,加上参数"utf-8",设置编码,可以防止中文乱码
String getName():获取上传的文件名,表单项是文件类型才能使用
write(文件路径):将上传的文件写到对应的硬盘地址中
jar包方法在Servlet中的使用
① 判断当前表单数据是否多段格式,如果不是就不能进行文件数据解析,下列操作都是在多段格式数据下进行
② 先创建一个表单项工厂类对象FileItemFactory
③ 根据创建的表单项工厂类对象创建出ServletFileUpload对象,用于解析数据
④ 调用servletFileUpload的解析方法,对表单数据进行解析,获取数据段集合,即表单项集合List(会有异常,需要捕获)
⑤ 遍历获取的表达项集合,判断每一个表单项类型,如果是普通类型表单项就只打印表单项名和表单项值,如果是文件类型表单项,就打印表单项名和文件名,同时将上传的文件写到指定位置

@WebServlet("/fileUpload")
public class FileUploadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("文件上传成功!");

        // 1. 判断当前表单是否是多段格式
        if (ServletFileUpload.isMultipartContent(req)) {
            // 2. 创建表单项工厂对象
            FileItemFactory fileItemFactory = new DiskFileItemFactory();

            // 3. 根据工厂对象创建出ServletFileUpload对象
            ServletFileUpload fileUpload = new ServletFileUpload(fileItemFactory);

            try {
                // 4. 调用方法解析数据,获取表单项集合
                List<FileItem> fileItems = fileUpload.parseRequest(req);

                // 5. 遍历表单项集合,对不同类型的表单项做不同处理
                for (FileItem item : fileItems) {
                    // 5.1 判断每一个表单项的类型,是否是普通类型
                    if (item.isFormField()) {
                        // 普通类型表单项
                        System.out.println("表单项名:" + item.getFieldName());
                        System.out.println("表单项值:" + item.getString("utf-8"));
                    } else {
                        // 不是普通类型表单项
                        System.out.println("文件表单项名:" + item.getFieldName());
                        System.out.println("文件名:" + item.getName());

                        // 将上传的文件写到指定位置,文件名不变
                        item.write(new File(
                            "D:\\ideaProject\\workplace_java\\Pro_FileUpAndDown\\src\\main\\webapp\\" +
                                            item.getName()));
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }
}

二、文件下载
参考博文

java web文件下载功能实现
web项目中各种路径的获取
2.1 文件下载的简单实现
2.1.1 通过超链接下载
创建download_href.jsp(或html),创建两个超链接,分别对应压缩包资源和图片资源

PS:两个文件都在工程的webapp/file目录下,这里的href的值是绝对路径,客户端的绝对路径都是相对于服务器根目录(http://ip地址:端口号),而不是工程中的webapp目录,所以需要加上工程路径(虚拟目录),推荐使用<%= request.getContextPath()%>/动态获取工程路径

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>通过超链接下载文件</title>
    </head>
    <body>
        <%-- 创建两个超链接 --%>
        <a href="<%= request.getContextPath()%>/file/like.rar">压缩包下载</a> <br/>
        <a href="<%= request.getContextPath()%>/file/doge.png">图片下载</a>
    </body>
</html>

在浏览器中直接点击对应链接,下载对应文件

PS:一般这种方法实现文件下载是不可行的,因为很多浏览器可以识别该文件格式,识别后就会直接打开对应的超链接(打开后一般是错误页面)。只有浏览器不能识别该文件格式的时候,才能实现下载

2.1.2 跳转到后台Servlet实现下载
创建download.jsp页面,通过点击对应超链接,跳转到FileDownloadServlet(资源路径为/fileDownload)中,并带上请求参数,将文件名传给后台

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>文件下载</title>
    </head>
    <body>
        <%-- 在后台通过Servlet下载文件 --%>
        <a href="<%= request.getContextPath()%>/fileDownload?filename=like.rar">压缩包下载</a> <br/>
        <a href="<%= request.getContextPath()%>/fileDownload?filename=doge.png">图片下载</a>
    </body>
</html>

在FileDownloadServlet中,先获取请求参数中的文件名,再通过ServletContext对象获取文件名对应的MIME类型,然后将对应的MIME类型设置为响应时的数据类型,再然后通过设置响应头Content-Disposition为attachment,告知浏览器客户端对数据的进行下载的操作,最后获取文件的位置,并通过输入流和输入流将文件写到浏览器端(使用jar包中的工具类实现更方便)

PS1:如果不设置响应头,在浏览器端就不能实现下载的功能,只会将图片展示到页面

PS2:服务器端的绝对路径指的是相对于工程的webapp目录,即http://ip地址:端口号/工程路径,所以在获取文件的位置时只需写成/file/文件名;另一种解释为相对路径/file/文件名中的第一个/被服务器解析成http://ip地址:端口号/工程路径,并映射到工程的webapp目录下

PS3:在获取文件名时,有两种方法

通过servletContext对象直接调用InputStream getResourceAsStream(文件位置),可以直接将文件位置转成输入流
先通过servletContext对象调用String getRealPath(文件位置),获取文件的真实路径,再通过FileInputStream(文件真实路径),获取输入流

@WebServlet("/fileDownload")
public class FileDownloadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, 
                          HttpServletResponse resp) throws ServletException, IOException {
        // 1. 获取请求参数中的文件名
        String filename = req.getParameter("filename");
        System.out.println(filename);

        // 2. 获取ServletContext对象,用于获取文件对应的MIME类型
        ServletContext servletContext = getServletContext();

        // 3. 获取文件名对应的MIME类型,这里直接写filename也可
        String mimeType = servletContext.getMimeType("/file/" + filename);
        System.out.println(mimeType);

        // 4. 将对应的MIME类型设置为响应时的数据类型
        resp.setContentType(mimeType);

        // 5. 设置响应头,告知浏览器要对文件做下载的操作,这里文件名可以自定义,不一定与原文件一样
        resp.setHeader("Content-Disposition", "attachment;filename=" + filename);

        // 6. 获取文件的路径,并转成输入流
        InputStream inOfFile = servletContext.getResourceAsStream("/file/" + filename);

        // 7. 获取响应的输出流
        ServletOutputStream outOfFile = resp.getOutputStream();

        // 8. 通过IO工具类,直接将输入流复制到输出流,然后写到浏览器客服端(简单方便)
        IOUtils.copy(inOfFile, outOfFile);
    }

    @Override
    protected void doGet(HttpServletRequest req, 
                         HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }
}

遇到的小问题:第一次运行,在浏览器点击超链接后,显示“无法访问此网站”,而且服务器收到多次请求。通过debug模式检查后,发现每次获取的文件输入流inOfFile值都为null,而且target文件夹中也没有图片和压缩包的资源,原因可能是webapp目录下的资源没有加载成功

解决方法:执行一次maven的clean命令,再重启服务器,就能解决问题

2.2 文件名的中文乱码问题
引入问题:在设置Content-Disposition响应头时,可以设置与原文件不一样的文件名,如果设置为中文名,会出现中文乱码的问题,导致下载到本地的文件名是乱码

2.2.1 使用URL编码
适用浏览器:谷歌、IE

使用:将resp.setHeader(“Content-Disposition”, “attachment;filename=” + filename),中的filename换成URLEncoder.encode(“带中文的文件名”, “utf-8”);

resp.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("狗头.png", "utf-8"));

2.2.2 使用BASE64编码
适用浏览器:火狐

BASE64的简单测试

PS:BASE64编码类BASE64Encoder中的编码方法encode()不是静态方法,所以需要先创建出编码对象,才能调用;BASE64解码类BASE64Decoder也是一样

测试代码

@Test
 public void testBASE64() throws IOException {
     // 1. 定义一个带中文的字符串
     String content = "我爱中国!";
 
     // 2. 创建Base64编码对象
     BASE64Encoder encoder = new BASE64Encoder();
 
     // 3. 先将内容字符串转成字节
     byte[] bytes = content.getBytes(StandardCharsets.UTF_8);
 
     // 4. 调用编码方法对字节进行编码,获取编码后的字符串
     String encodeString = encoder.encode(bytes);
     System.out.println("编码结果:" + encodeString);
 
     // 5. 创建Base64解码对象
     BASE64Decoder decoder = new BASE64Decoder();
 
     // 6. 将编码的字符再转成字节数组
     byte[] decodeBuffer = decoder.decodeBuffer(encodeString);
 
     // 7. 获取解码后的字符串
     String decodeString = new String(decodeBuffer, StandardCharsets.UTF_8);
     System.out.println("解码结果:" + decodeString);
 }

测试结果
在这里插入图片描述

BASE64在文件下载中的使用:将resp.setHeader(“Content-Disposition”, “attachment;filename=” + filename),中的filename换成=?charset?B?XXXX?=

=?charset?B?XXXX?=的解释:

=?:表示编码内容的开始
charset:字符集,如utf-8
B:表示BASE64编码,不用修改
XXXX:表示通过BASE64编码之后的内容
?=:表示编码内容的结束

// 先获取BASE64编码后的内容
String encodeString  = new BASE64Encoder.encode("狗头.png".getBytes(StandardCharsets.UTF_8));

// 再设置响应头
resp.setHeader("Content-Disposition", "attachment;filename==?utf-8?B?" + encodeString + "?=");

2.2.3 通过User-Agent请求头选择使用对应编码方式
PS:因为不同浏览器适用不用的编码方式,为统一解决所有浏览器的文件名中文乱码问题,可以通过User-Agent请求头来判断浏览器的版本,从而使用对应的编码方式

// 5. 设置响应头,告知浏览器要对文件做下载的操作
// 获取User-Agent请求头,判断浏览器种类
String reqHeader = req.getHeader("User-Agent");
// 判断请求头中是否包含火狐的字样
if (reqHeader.contains("Firefox")) {
    // 如果是火狐浏览器,就使用BASE64编码
    // 先使用BASE64对内容进行编码,再插入
    String encodeString = new BASE64Encoder().encode("狗头.png".getBytes(StandardCharsets.UTF_8));
    resp.setHeader("Content-Disposition", "attachment;filename==?utf-8?B?" + encodeString + "?=");
} else {
    // 如果是非火狐浏览器,如IE、谷歌,就使用URL编码即可
    resp.setHeader("Content-Disposition", "attachment;filename=" + 
                   URLEncoder.encode("狗头.png", "utf-8"));
}

参考文章:http://blog.ncmem.com/wordpress/2023/11/27/javaweb%e5%ae%9e%e7%8e%b0%e7%ae%80%e5%8d%95%e7%9a%84%e6%96%87%e4%bb%b6%e4%b8%8a%e4%bc%a0%e5%92%8c%e4%b8%8b%e8%bd%bd/
欢迎入群一起讨论

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是文件上传下载的完整代码,包括JSP和Servlet: 上传文件的JSP页面(upload.jsp): ``` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>文件上传</title> </head> <body> <h2>文件上传</h2> <form action="UploadServlet" method="post" enctype="multipart/form-data"> <label>请选择文件:</label> <input type="file" name="file"><br/><br/> <input type="submit" value="上传"> </form> </body> </html> ``` 上传文件的Servlet(UploadServlet.java): ``` import java.io.File; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; @WebServlet("/UploadServlet") public class UploadServlet extends HttpServlet { private static final long serialVersionUID = 1L; public UploadServlet() { super(); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); // 上传文件保存路径 String savePath = this.getServletContext().getRealPath("/WEB-INF/upload"); // 创建保存路径的文件夹 File file = new File(savePath); if (!file.exists() && !file.isDirectory()) { System.out.println(savePath + "目录不存在,需要创建"); file.mkdir(); } // 获取上传的文件 Part part = request.getPart("file"); // 获取上传的文件名 String fileName = part.getSubmittedFileName(); // 上传的文件保存到服务器的完整路径 String filePath = savePath + File.separator + fileName; // 将上传的文件保存到服务器指定的路径中 part.write(filePath); out.println("文件上传成功!"); } } ``` 下载文件的Servlet(DownloadServlet.java): ``` import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/DownloadServlet") public class DownloadServlet extends HttpServlet { private static final long serialVersionUID = 1L; public DownloadServlet() { super(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取要下载的文件名 String fileName = request.getParameter("filename"); // 设置文件MIME类型 response.setContentType(getServletContext().getMimeType(fileName)); // 设置Content-Disposition response.setHeader("Content-Disposition", "attachment;filename=" + fileName); // 读取目标文件,通过response将目标文件写到客户端 // 获取目标文件的绝对路径 String fullFileName = getServletContext().getRealPath("/WEB-INF/upload/" + fileName); // 读取文件 FileInputStream in = new FileInputStream(fullFileName); OutputStream out = response.getOutputStream(); // 写文件 int len = 0; byte[] buffer = new byte[1024]; while ((len = in.read(buffer)) > 0) { out.write(buffer, 0, len); } in.close(); out.close(); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } ``` 在web.xml文件中添加以下配置: ``` <servlet> <servlet-name>DownloadServlet</servlet-name> <servlet-class>DownloadServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>DownloadServlet</servlet-name> <url-pattern>/DownloadServlet</url-pattern> </servlet-mapping> ``` 在JSP页面中,可以通过以下方式链接到下载Servlet: ``` <a href="DownloadServlet?filename=xxx.txt">下载xxx.txt文件</a> ``` 其中,xxx.txt是要下载的文件名。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值