09-upload_download

  • 希望将本地的资源发送给服务器,服务器提供给其他人使用、查看
  • 将本地的资源上传至服务器,其他的电脑中可以通过访问网站来获取上传资源,打破空间局限性。eg:云存储、云编辑
18

1. fileUploadLocal

  1. 在页面中显示一个按钮
    • 用户点击按钮后选择本地要上传的文件,在页面中使用input标签,type值设置为file即可
  2. 确定上传请求的发送方式
    • 上传成功后的响应结果在当前页面显示,使用ajax请求来完成资源的发送
  3. 上传请求的请求数据及数据格式
    1. 请求数据:上传的文件本身、用户名、Id、密码等,建议上传功能中不携带除上传资源以外的数据
    2. 数据格式:
      • 传统的请求中,请求数据是以键值对的格式发送给后台服务器
      • 上传请求中,没有任何一对键值可以描述上传的数据,因为数据本身是非常大的。键就相当于一个变量,使用一个变量存储一个10G的电影显然是不可能的。在上传请求中,将请求数据以二进制流的方式发送给服务器
  4. ajax中如何发送二进制流数据给服务器
    1. 创建FormData对象,将请求数据存储到该对象中发送
    2. processData属性值设置为false,告诉浏览器发送对象请求数据
      • 默认值true。默认情况下,通过data选项传递数据,如果是一个对象(技术上讲只要不是字符串),都会处理转化成一个查询字符串,以配合默认内容类型application/x-www-form-urlencoded。如果要发送DOM树信息或其它不希望转换的信息,设置为false
    3. contentType属性值设置为false,设置请求数据的类型为二进制类型
  5. 上传成功后,后台服务器响应json对象给浏览器
  6. 文件上传依赖的jar
<!-- 文件上传依赖 -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.8.0</version>
</dependency>



 




 


  1. spring_mvc.xml
<!-- 配置文件上传解析组件
        id值必须是multipartResolver
        SpringMVC默认通过该id找到该组件
-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="5242880"/>
</bean>




 


  1. 准备用户表
-- auto-generated definition
create table mvc_player
(
    id       int auto_increment comment '主键' primary key,
    name     varchar(20)  null comment '账号',
    password varchar(20)  null comment '密码',
    nickname varchar(20)  null comment '玩家昵称',
    photo    varchar(255) null comment '玩家头像',
    filetype varchar(255) null comment '文件类型'
);
  1. 前端代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>playerAdd.jsp</title>

    <script type="text/javascript" src="js/jquery.min.js"></script>
    <script type="text/javascript">
        $(function () {
            $("#uploadFile").click(function () {
                // 获取要上传的文件
                var photoFile = $("#photo")[0].files[0]
                if (photoFile == undefined) {
                    alert("您还未选中文件")
                    return;
                }
                // 将文件装入FormData对象
                var formData = new FormData();
                formData.append("headPhoto", photoFile)
                // ajax向后台发送文件
                $.ajax({
                    type: "post",
                    data: formData,
                    url: "fileUploadLocal",
                    processData: false,
                    contentType: false,
                    success: function (result) {
                        // 接收后台响应的信息
                        console.log(result)
                        // 图片回显
                    }
                })
            })
        })
    </script>
</head>
<body>
<form action="addPlayer" method="get">
    <p>账号<input type="text" name="name"></p>
    <p>密码<input type="text" name="password"></p>
    <p>昵称<input type="text" name="nickname"></p>
    <p>头像:</p>
    <br/>
    <input id="photo" type="file">
    <a id="uploadFile" href="javascript:void(0)">立即上传</a>
    <p><input type="submit" value="注册"></p>
</form>
</body>
</html>
















 
 
























 
 




  1. Controller代码
package com.listao.mvc.controller;

@Controller
public class FileController {

    /**
     * 文件名乱码。web.xml已经配置CharacterEncodingFilter
     */
    @ResponseBody
    @RequestMapping("/fileUploadLocal")
    public String fileUploadLocal(MultipartFile headPhoto) throws IOException {
        // 指定文件存储目录
        File imgs = new File("/Users/listao/Pictures/imgs");
        // 获取文件名
        String originalFilename = headPhoto.getOriginalFilename();
        // 文件存储位置
        File file = new File(imgs, originalFilename);
        // 文件保存
        headPhoto.transferTo(file);

        return "ok";
    }
}










 
 
 
 
 
 
 
 
 




2. fileUploadWebapp

访问项目中的资源:http://localhost:8080/spring_mvc_war_exploded/js/jquery.min.js

文件上传中的几个问题

  1. 中文文件名编码问题:已经通过过滤器解决
  2. 文件位置存储问题:放在当前项目下,作为静态资源,可以通过URL访问
  3. 前端页面效果展现

1. spring_mvc.xml

<!-- 静态资源放行 -->
<mvc:resources mapping="/upload/**" location="/upload/"/>

2. 回显java

/**
 * 1. 前端上传图片回显
 * 2. 文件存储目录 => 项目部署环境下的upload目录
 * 3. UUID解决文件名冲突
 * 4. 控制上传文件类型
 * 5. 规范返回值类型
 * 6. 控制文件大小
 */
@ResponseBody
@RequestMapping("/fileUploadWebapp")
public Map<String, String> fileUploadWebapp(MultipartFile headPhoto, HttpServletRequest req) throws IOException {
    Map<String, String> map = new HashMap<>();

    // 控制文件大小
    if (headPhoto.getSize() > 1024 * 1024 * 5) {
        map.put("message", "文件大小不能超过5M");
        return map;
    }

    // 指定文件存储目录 => 项目部署环境下的upload目录
    String realPath = req.getServletContext().getRealPath("/upload");
    File imgs = new File(realPath);
    if (!imgs.exists()) {
        imgs.mkdir();
    }
    // 获取文件名
    String originalFilename = headPhoto.getOriginalFilename();
    // 避免文件名冲突,使用UUID替换文件名
    String uuid = UUID.randomUUID().toString();
    // 获取拓展名
    String extendsName = originalFilename.substring(originalFilename.lastIndexOf("."));
    // 控制文件类型
    if (!extendsName.equals(".jpg")) {
        map.put("message", "文件类型必须是.jpg");
        return map;
    }

    // 新的文件名
    String newFileName = uuid.concat(extendsName);

    // 文件存储位置
    File file = new File(imgs, newFileName);
    // 文件保存
    headPhoto.transferTo(file);

    // 上传成功之后,把文件的名字和文件的类型返回给浏览器
    map.put("message", "上传成功");
    map.put("newFileName", newFileName);
    map.put("filetype", headPhoto.getContentType());
    return map;
}




















 
 



















 

 







1. 控制文件大小

  • 文件上传解析组件控制。会出现异常,后期可以配置一个异常解析器解决
<!-- 配置文件上传解析组件
        id值必须是multipartResolver
        SpringMVC默认通过该id找到该组件
-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 1024 * 1024 * 5 = 5M -->
    <property name="maxUploadSize" value="5242880"/>
</bean>






 

3. 图片回显jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>playerAdd.jsp</title>

    <script type="text/javascript" src="js/jquery.min.js"></script>
    <script type="text/javascript">
        $(function () {
            $("#uploadFile").click(function () {
                // 获取要上传的文件
                var photoFile = $("#photo")[0].files[0]
                if (photoFile === undefined) {
                    alert("您还未选中文件")
                    return;
                }
                // 将文件装入FormData对象
                var formData = new FormData();
                formData.append("headPhoto", photoFile)
                // ajax向后台发送文件
                $.ajax({
                    type: "post",
                    data: formData,
                    url: "fileUploadWebapp",
                    processData: false,
                    contentType: false,
                    success: function (result) {
                        // 接收后台响应的信息
                        alert(result.message)
                        // 图片回显
                        $("#headImg").attr("src", "upload/" + result.newFileName);
                    }
                })
            })
        })
    </script>
</head>
<body>
<form action="addPlayer" method="get">
    <p>账号<input type="text" name="name"></p>
    <p>密码<input type="text" name="password"></p>
    <p>昵称<input type="text" name="nickname"></p>
    <p>头像:</p>
    <br/>
    <input id="photo" type="file">
    <br/>
    <%-- 图片回显 --%>
    <img id="headImg" style="width: 200px;height: 200px" alt="你还未上传图片">
    <br/>
    <a id="uploadFile" href="javascript:void(0)">立即上传</a>
    <p><input type="submit" value="注册"></p>
</form>
</body>
</html>





























 















 
 






4. 进度条

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>playerAdd.jsp</title>

    <style>
        .progress {
            width: 200px;
            height: 10px;
            border: 1px solid #ccc;
            border-radius: 10px;
            margin: 10px 0px;
            overflow: hidden;
        }

        /* 初始状态设置进度条宽度为0px */
        .progress > div {
            width: 0px;
            height: 100%;
            background-color: yellowgreen;
            transition: all .3s ease;
        }
    </style>
    <script type="text/javascript" src="static/js/jquery.min.js"></script>
    <script type="text/javascript">
        $(function () {
            $("#uploadFile").click(function () {
                // 获取要上传的文件
                var photoFile = $("#photo")[0].files[0]
                if (photoFile === undefined) {
                    alert("您还未选中文件")
                    return;
                }
                // 将文件装入FormData对象
                var formData = new FormData();
                formData.append("headPhoto", photoFile)
                // ajax向后台发送文件
                $.ajax({
                    type: "post",
                    data: formData,
                    url: "fileUpload.do",
                    processData: false,
                    contentType: false,
                    success: function (result) {
                        // 接收后台响应的信息
                        alert(result.message)
                        // 图片回显
                        $("#headImg").attr("src", "upload/" + result.newFileName);
                    },
                    xhr: function () {
                        var xhr = new XMLHttpRequest();
                        // 使用XMLHttpRequest.upload监听上传过程,注册progress事件,打印回调函数中的event事件
                        xhr.upload.addEventListener('progress', function (e) {
                            // loaded代表上传了多少
                            // total代表总数为多少
                            var progressRate = (e.loaded / e.total) * 100 + '%';
                            // 通过设置进度条的宽度达到效果
                            $('.progress > div').css('width', progressRate);
                        })
                        return xhr;
                    }
                })
            })
        })
    </script>
</head>
<body>
<form action="addPlayer" method="get">
    <p>账号<input type="text" name="name"></p>
    <p>密码<input type="text" name="password"></p>
    <p>昵称<input type="text" name="nickname"></p>
    <p>头像:</p>
    <br/>
    <input id="photo" type="file">
    <%-- 图片回显 --%>
    <br/>
    <img id="headImg" style="width: 200px;height: 200px" alt="你还未上传图片">
    <br/>
    <%-- 进度条 --%>
    <div class="progress">
        <div></div>
    </div>
    <a id="uploadFile" href="javascript:void(0)">立即上传</a>
    <p><input type="submit" value="注册"></p>
</form>
</body>
</html>






 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 



























 
 
 
 
 
 
 
 
 
 
 
 

















 
 
 
 





3. fileUploadServer

分服务器处理目的:让服务器各司其职,从而提高项目的运行效率(注意:此处说的不是服务器集群)

  • 数据库服务器:运行数据库
  • 缓存和消息服务器:负责处理大并发访问的缓存和消息
  • 文件服务器:负责存储用户上传文件的服务器
  • 应用服务器:负责部署应用

分服务器工作示意图

db195ee67b854a7b855c7e70ba70c233

1. 文件服务器

  • 单独启动一个Tomcat实例作为文件服务器
  1. 设置远程服务器端口号:Server、Connector

Tomcat:conf > server.xml

<!-- 8005改为8006 -->
<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />

 

<!-- 8080改为8090 -->
<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443"
           maxParameterCount="1000"
           />

 




  1. 远程服务器中设置非只读

Tomcat:conf > web.xml

<servlet>
    <servlet-name>default</servlet-name>
    <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
    <init-param>
        <param-name>debug</param-name>
        <param-value>0</param-value>
    </init-param>
    <init-param>
        <param-name>listings</param-name>
        <param-value>false</param-value>
    </init-param>
    <!-- 增加readonly配置 -->
    <init-param>
        <param-name>readonly</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>











 
 
 
 
 


  1. webapps下创建一个upload目录
  2. 访问新实例http://ip:8090

2. idea_Tomcat

  1. web.xml中设置非只读
  2. http://localhost:8080/upload/a.jpg
image-20240101160106917

3. SC

<!-- 跨服务文件上传依赖 -->
<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-client</artifactId>
    <version>1.19</version>
</dependency>



 


    // 文件存储位置
    private final static String FILE_SERVER = "http://127.0.0.1:8080/upload/";

    @ResponseBody
    @RequestMapping("/fileUploadServer")
    public Map<String, String> fileUploadServer(MultipartFile headPhoto, HttpServletRequest req) throws IOException {
        Map<String, String> map = new HashMap<>();
        // 获取文件名
        String originalFilename = headPhoto.getOriginalFilename();
        // 避免文件名冲突,使用UUID替换文件名
        String uuid = UUID.randomUUID().toString();
        // 获取拓展名
        String extendsName = originalFilename.substring(originalFilename.lastIndexOf("."));
        // 新的文件名
        String newFileName = uuid.concat(extendsName);
        // 创建sun公司提供的jersey包中的client对象
        Client client = Client.create();
        WebResource resource = client.resource(FILE_SERVER + newFileName);
        // 文件保存到另一个服务器上去了
        resource.put(String.class, headPhoto.getBytes());
        // 上传成功之后,把文件的名字和文件的类型返回给浏览器
        map.put("message", "上传成功");
        map.put("newFileName", newFileName);
        map.put("filetype", headPhoto.getContentType());
        return map;
    }

 













 
 
 
 
 






<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>playerAdd.jsp</title>

    <style>
        .progress {
            width: 200px;
            height: 10px;
            border: 1px solid #ccc;
            border-radius: 10px;
            margin: 10px 0px;
            overflow: hidden;
        }

        /* 初始状态设置进度条宽度为0px */
        .progress > div {
            width: 0px;
            height: 100%;
            background-color: yellowgreen;
            transition: all .3s ease;
        }
    </style>

    <script type="text/javascript" src="js/jquery.min.js"></script>
    <script type="text/javascript">
        $(function () {
            $("#uploadFile").click(function () {
                // 获取要上传的文件
                // $("#photo")为jquery对象,获取dom对象
                const photoFile = $("#photo")[0].files[0];
                if (photoFile === undefined) {
                    alert("您还未选中文件")
                    return;
                }

                // 将文件装入FormData对象
                const formData = new FormData();
                formData.append("headPhoto", photoFile)
                // ajax向后台发送文件
                $.ajax({
                    type: "post",
                    data: formData,
                    url: "fileUploadServer",
                    processData: false,
                    contentType: false,
                    success: function (result) {
                        // 接收后台响应的信息
                        alert(result.message)
                        // 1. 图片回显
                        $("#headImg").attr("src", "http://127.0.0.1:8080/upload/" + result.newFileName);
                        // 2. 将文件类型和文件名放入form表单
                        $("#photoInput").val(result.newFileName)
                        $("#filetypeInput").val(result.filetype)
                    },
                    xhr: function () {
                        var xhr = new XMLHttpRequest();
                        // 使用XMLHttpRequest.upload监听上传过程,注册progress事件,打印回调函数中的event事件
                        xhr.upload.addEventListener('progress', function (e) {
                            // loaded代表上传了多少
                            // total代表总数为多少
                            var progressRate = (e.loaded / e.total) * 100 + '%';
                            // 通过设置进度条的宽度达到效果
                            $('.progress > div').css('width', progressRate);
                        })
                        return xhr;
                    }
                })
            })
        })
    </script>
</head>
<body>

<form action="addPlayer" method="get">
    <p>账号:<input type="text" name="name"></p>
    <p>密码:<input type="text" name="password"></p>
    <p>昵称:<input type="text" name="nickname"></p>
    <p>头像:</p>
    <hr/>
    <input id="photo" type="file"/>
    <%-- 图片回显 --%>
    <hr/>
    <img id="headImg" style="width: 200px;height: 200px" alt="你还未上传图片"/>
    <hr/>
    <%-- 进度条 --%>
    <div class="progress">
        <div></div>
    </div>
    <a id="uploadFile" href="javascript:void(0)">立即上传</a>
    <%-- 3. 使用隐藏的输入框存储文件名称和文件类型 --%>
    <input id="photoInput" type="hidden" name="photo"/>
    <input id="filetypeInput" type="hidden" name="filetype"/>

    <p><input type="submit" value="注册"></p>
</form>
</body>
</html>

















































 
 
 
 
 




































 
 
 





@Data
@AllArgsConstructor
@NoArgsConstructor
public class Player implements Serializable {
    private Integer id;
    private String name;
    private String password;
    private String nickname;
    private String photo;
    private String filetype;
}
package com.listao.mvc.controller;

@Controller
public class PlayerController  {

    @Autowired
    private PlayerService playerService;

    @RequestMapping("/addPlayer")
    public String addPlayer(Player player){
        playerService.addPlayer(player);
        return "redirect:/playerShow.jsp";
    }

    @RequestMapping("/getAllPlayer")
    @ResponseBody
    public List<Player> getAllPlayer(){
        return playerService.getAllPlayer();
    }

}

4. download

下载的基本流程

  • 用户发起请求,请求要下载的资源。服务器根据请求,将其硬盘中的文件资源发送给浏览器的过程
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>playerShow.jsp</title>

    <style>
        #playerTable {
            width: 50%;
            border: 3px solid cadetblue;
            margin: 0 auto;
            text-align: center;
        }

        #playerTable th, td {
            border: 1px solid rgb(128, 128, 128);
        }

        #playerTable img {
            width: 100px;
            height: 100px;
        }
    </style>

    <script type="text/javascript" src="js/jquery.min.js"></script>
    <script>
        $(function () {
            $.ajax({
                type: "get",
                url: "getAllPlayer",
                success: function (players) {
                    $.each(players, function (i, e) {
                        $("#playerTable").append('<tr>\n' +
                            '        <td>' + e.id + '</td>\n' +
                            '        <td>' + e.name + '</td>\n' +
                            '        <td>' + e.password + '</td>\n' +
                            '        <td>' + e.nickname + '</td>\n' +
                            '        <td>\n' +
                            '            <img src="http://127.0.0.1:8080/upload/' + e.photo + '" alt="图片未找到">\n' +
                            '        </td>\n' +
                            '        <td>\n' +
                            '            <a href="fileDownload?photo=' + e.photo + '&filetype=' + e.filetype + '">下载</a>\n' +
                            '        </td>\n' +
                            '    </tr>')
                    })
                }
            })

        })
    </script>
</head>
<body>
<table id="playerTable" cellspacing="0xp" cellpadding="0px">
    <tr>
        <th>编号</th>
        <th>用户名</th>
        <th>密码</th>
        <th>昵称</th>
        <th>头像</th>
        <th>操作</th>
    </tr>
</table>

</body>
</html>








































 























    @RequestMapping("/fileDownload")
    public void fileDownLoad(String photo, String filetype, HttpServletResponse response) throws IOException {
        // 设置响应头
        // 通知浏览器将数据保存到磁盘上,不在浏览器上直接解析
        response.setHeader("Content-Disposition", "attachment;filename=" + photo);
        // 通知浏览器下载的文件类型
        response.setContentType(filetype);
        // 获取一个文件的输入流
        InputStream inputStream = new URL(FILE_SERVER + photo).openStream();
        // 获取一个指向浏览器的输出流
        ServletOutputStream outputStream = response.getOutputStream();
        // 向浏览器响应文件
        IOUtils.copy(inputStream, outputStream);
    }