JavaWeb 文件上传和下载

简介: JavaWeb——文件上传与下载 内容分享。

目录

一、文件上传

       1.文件上传和下载的使用说明 :

       2.文件上传基本原理 :

       3.文件上传经典案例 :

           3.1 页面实现:

           3.2 servlet实现 :

           3.3 工具类实现 :

           3.4 运行测试 :

           3.5 注意事项 :

二、文件下载

       1.文件下载基本原理 :

       2.文件下载经典案例 :

           2.1 准备工作

           2.2 页面实现

           2.3 servlet实现

           2.4 运行测试

           2.5 注意事项


一、文件上传

       1.文件上传和下载的使用说明 :

       web文件上传和下载需要用到三个jar包(fileupload2版本, tomcat10),如下图所示 :

image.gif 编辑

       2.文件上传基本原理 :

       客户端——

       (1) 使用表单提交数据

      (2) 表单属性action仍然指向目标web资源(数据提交给该web资源)

      (3) 表单属性method指定为post

       (4) 表单属性enctype,指encodetype,编码类型;默认为application/x-www-form-urlencoded,即url编码,这种编码不适合二进制文件数据的提交,一般是适用于文本数据。若想进行二进制文件的提交,需要指定enctype为multipart/form-data,表示表单提交的数据是由多个部分组成,也就是可以提交二进制数据和文本数据。

      服务端——

       (1) 判断是否为一个文件表单;

       (2) 判断文件表单所提交的各个表单项分别是什么类型;

      (3) 如果是一个普通文本表单项,就按照文本的方式来处理;如果是一个文件表单项(二进制数据) ,就使用IO技术进行处理

       (4) 把表单提交的文件数据,保存到指定的服务端的某个目录。

       3.文件上传经典案例 :

           3.1 页面实现:

               页面效果如下 :

image.gif编辑

               upload.jsp代码如下 :

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <base href="<%=request.getContextPath()+"/"%>>">
    <style type="text/css">
        table, td {
            border:2px pink dashed;
            border-collapse: collapse;
            padding: 5px
        }
        img {
            border-radius: 50%;
        }
        input[type="file"] {
            position: absolute;
            left: 0;
            top: 0;
            height: 200px;
            opacity: 0;
            cursor: pointer
        }
        input[type="submit"] {
            width: 150px
        }
        #td01 {
            width: 100px;
            text-align: center
        }
    </style>
    <script type="text/javascript">
        function prev(event) {
            //获取展示图片的区域
            var img = document.getElementById("prevView");
            //获取文件对象
            var file = event.files[0];
            //获取文件阅读器(JS的一个类FileReader)
            var reader = new FileReader();
            reader.readAsDataURL(file);
            reader.onload = function () {
                //给img的src设置图片url
                img.setAttribute("src", this.result);
            }
        }
    </script>
</head>
<body>
<!--
    form表单属性的注意事项 :
    (1) action : 注意此页面中base标签的指定
    (2) enctype="multipart/form-data", 表示提交的数据是多个部分构造,有文件和文本
 -->
<form action="fileUploadServlet" method="post" enctype="multipart/form-data">
    <table width="350px">
        <tr>
            <td>上传的图片:</td>
            <td><img src="2.jpg" alt="" width="200" height="200" id="prevView">
                <input type="file" name="pic" id="" value="" onchange="prev(this)"/>
            </td>
        </tr>
        <tr>
            <td>文件名(self-def):</td>
            <td><input type="text" name="name"><br/></td>
        </tr>
        <tr>
            <td colspan="2" id="td01"><input type="submit" value="Submit"/></td>
        </tr>
    </table>
</form>
</body>
</html>

image.gif

           3.2 servlet实现 :

              定义一个servlet来实现文件上传的功能,需要用到导入的jar包的工具类。FileUploadServlet类代码如下 :

package servlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload2.core.DiskFileItem;
import org.apache.commons.fileupload2.core.DiskFileItemFactory;
import org.apache.commons.fileupload2.jakarta.JakartaServletFileUpload;
import utils.WebUtils;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.UUID;
/**
 * @author : Cyan_RA9
 * @version : 21.0
 */
@WebServlet(urlPatterns = {"/fileUploadServlet"})
public class FileUploadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
        //1.判断是否为一个文件表单
        if (JakartaServletFileUpload.isMultipartContent(req)) {
            //2.创建一个DiskFileItemFactory对象(注意创建方式同低版本不同)
            DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory.Builder().get();
            //3.利用得到的diskFileItemFactory对象,创建一个解析上传数据的工具对象
            JakartaServletFileUpload<DiskFileItem, DiskFileItemFactory> jakartaServletFileUpload =
                    new JakartaServletFileUpload<>(diskFileItemFactory);
            jakartaServletFileUpload.setHeaderCharset(StandardCharsets.UTF_8);  //乱码问题
            //4.利用工具对象,将表单提交的数据(text or file) 封装到DiskFileItem中
            try {
                List<DiskFileItem> diskFileItems = jakartaServletFileUpload.parseRequest(req);
                //5.遍历获取到的集合
                for (DiskFileItem diskFileItem : diskFileItems) {
                    //6.判断是否为文件类型(true表示不是文件,是input type=text)
                    if (diskFileItem.isFormField()) {
                        String name = diskFileItem.getString(StandardCharsets.UTF_8);
                        System.out.println("文件名(self-def) = " + name);
                    } else {    //false表示是文件
                        //7.如果判断为是文件,就将上传的文件保存到本地。
                        String filePath = "/upload/";
                            //通过ServletContext对象,得到真实的工作路径
                        String realPath = req.getServletContext().getRealPath(filePath);
                        System.out.println("realPath = " + realPath);
                        String fileName = diskFileItem.getName();
                        System.out.println("fileName = " + fileName);
                        //8.由于实际工作的路径与web路径不同,所以需要创建目录
                            //利用定义的工具类,创建分级目录,提高程序执行的效率。
                        String finalPath = realPath + WebUtils.getLocalDate();
                        File file = new File(finalPath);
                        if (!file.exists()) {
                            file.mkdirs();
                        }
                        //9.将上传的文件拷贝到服务端的指定目录
                            /*
                                (1) 注意得到Path的两种方式
                                (2) 解决文件重名问题,给文件名加一个前缀,保证是唯一即可。
                             */
                        fileName = UUID.randomUUID().toString() + "_" + fileName;
                        diskFileItem.write(new File(finalPath + fileName).toPath());
                    }
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            System.out.println("不是一个表单捏~");
        }
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }
}

image.gif

           3.3 工具类实现 :

               定义一个工具类,用来对文件名作标识,以解决文件重名导致的文件覆盖问题。

               WebUtils类代码如下 :

package utils;
import java.time.LocalDateTime;
/**
    @function : 该方法返回一个日期形式的字符串路径
 */
public class WebUtils {
    public static String getLocalDate() {
        LocalDateTime ldt = LocalDateTime.now();
        int year = ldt.getYear();
        int month = ldt.getMonthValue();   //获取月份的值
        int dayOfMonth = ldt.getDayOfMonth();
        String localDate = year + "/" + month + "/" + dayOfMonth + "/";
        return localDate;
    }
}

image.gif

           3.4 运行测试 :

               测试结果如下GIF :

image.gif编辑

           3.5 注意事项 :

      (1) 同一个目录下上传了很多文件时,会造成访问文件速度变慢的问题,因此可以将文件上传到不同的目录,例如按照上传日期进行分类, eg : 2023/9/2/。

       (2) 一次完美的文件上传需要考虑很多因素,eg : 断点续传,对图片大小和尺寸的限制,分片上传,防止恶意上传等。PS : 在项目中,可以考虑百度开发的WebUploader组件。

       (3) 文件上传功能在项目中应该有限制的使用,一般用在头像、证明、合同、产品展示等;如果不加限制,会造成服务器空间被大量占用(eg : 微信朋友圈最多一次发9张)。

      (4)Tomcat在启动时,不会为web目录下的空文件夹在out目录下创建对应的文件夹,所以,需要在空文件夹中事先放入一个文件。


二、文件下载

       1.文件下载基本原理 :

       服务器端要设置两个重要的HTTP响应头——

       (1) Content-Disposition : 表示下载数据的展示方式,比如是内联形式(即网页形式或者网页的一部分),或者是文件下载方式(attachment)。

       (2) Content-Type : 指定返回数据的MIME类型。        

       而响应体,在网络传输时是图片的原生数据(即按照浏览器下载的编码),而下载后查看到的是浏览器本身解析后的文件

       2.文件下载经典案例 :

           2.1 准备工作

               将要下载的文件拷贝到web目录的sources目录下,如下图所示 :

image.gif编辑

          2.2 页面实现

               页面效果如下 :

image.gif编辑

              download.jsp代码如下 :

<%--
    User : Cyan_RA9
    Version : 21.0
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <meta charset="UTF-8"/>
    <title>Title</title>
    <base href="<%= request.getContextPath() + "/"%>"/>
</head>
<body>
<h1>文件下载</h1>
<a href="fileDownloadServlet?name=miku_flowers.jpg">点我下载初音未来</a> <br/><br/>
<a href="fileDownloadServlet?name=3.txt">点我下载王勃的滕王阁诗</a>
</body>
</html>

image.gif

           2.3 servlet实现

               定义一个servlet,完成文件下载的功能。

               FileDownloadServlet代码如下 :

package servlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
/**
 * @author : Cyan_RA9
 * @version : 21.0
 */
@WebServlet(urlPatterns={"/fileDownloadServlet"})
public class FileDownloadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.得到文件名和文件路径(文件资源的路径!)
        req.setCharacterEncoding("utf-8");
        String fileName = req.getParameter("name");
        String filePath = "/sources/" + fileName;
        //2.通过ServletContext对象得到文件的MIME类型
        String mimeType = req.getServletContext().getMimeType(filePath);
        System.out.println("mimeType = " + mimeType);
        //3.设置第一个响应头Content-Type
        resp.setContentType(mimeType);
        //4.设置第一个响应头Content-Disposition
            //Content-Disposition用于指定下载的数据的展示形式,其中attachment表示使用文件下载方式
            //不同浏览器需要的编码格式不同
        if (req.getHeader("User-Agent").contains("Chrome")) {
            resp.setHeader("Content-Disposition", "attachment; filename=" +
                    URLEncoder.encode(fileName, "UTF-8"));
        } else {
            //其他浏览器的编码操作
        }
        //5.读取文件,将读取到的文件返回给客户端(IO流拷贝的封装)
            //获取与数据源文件相关联的字节输入流
        InputStream resourceAsStream = req.getServletContext().getResourceAsStream(filePath);
            //获取与目的地文件相关联的字节输出流
        ServletOutputStream outputStream = resp.getOutputStream();
            //利用封装好的工具类,进行IO流拷贝
        IOUtils.copy(resourceAsStream, outputStream);
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }
}

image.gif

           2.4 运行测试

               运行效果如下图所示(GIF图)

image.gif编辑

           2.5 注意事项

       (1) 不同的浏览器,对于下载的文件的处理方式不同,有些是直接打开该文件,有些则是将文件下载到本地的指定下载目录。

       (2) 对于网站上的文件,很多文件可以使用“另存为”来下载;对于大文件(文档,视频等),往往使用专业的下载工具(eg : 迅雷,百度网盘等)。

      (3) 注意,不同的浏览器对文件解码格式的需求不同。

目录
相关文章
|
17天前
|
存储 Java API
Java实现导出多个excel表打包到zip文件中,供客户端另存为窗口下载
Java实现导出多个excel表打包到zip文件中,供客户端另存为窗口下载
25 4
|
23天前
|
Web App开发 Java
使用java操作浏览器的工具selenium-java和webdriver下载地址
【10月更文挑战第12天】Selenium-java依赖包用于自动化Web测试,版本为3.141.59。ChromeDriver和EdgeDriver分别用于控制Chrome和Edge浏览器,需确保版本与浏览器匹配。示例代码展示了如何使用Selenium-java模拟登录CSDN,包括设置驱动路径、添加Cookies和获取页面源码。
|
1月前
|
前端开发 Java 应用服务中间件
Javaweb学习
【10月更文挑战第1天】Javaweb学习
32 2
|
1月前
|
安全 Java Android开发
JavaWeb解压缩漏洞之ZipSlip与Zip炸弹
JavaWeb解压缩漏洞之ZipSlip与Zip炸弹
48 5
|
1月前
|
存储 前端开发 Java
Java后端如何进行文件上传和下载 —— 本地版(文末配绝对能用的源码,超详细,超好用,一看就懂,博主在线解答) 文件如何预览和下载?(超简单教程)
本文详细介绍了在Java后端进行文件上传和下载的实现方法,包括文件上传保存到本地的完整流程、文件下载的代码实现,以及如何处理文件预览、下载大小限制和运行失败的问题,并提供了完整的代码示例。
453 1
|
1月前
|
Java
java 文件上传和下载
java 文件上传和下载
21 0
|
9天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
18天前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?
|
5天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
25 9
|
8天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####