SSM-文件上传方案


    JavaWeb后端服务开发过程中,有对文件上传服务的需求。

    在之前的《Axios+SSM上传和获取图片》中,提供了图片上传和显示的解决方案,但是对于数据量更大的文件,虽然例如MySQL数据库提供了用于存储二进制文件的Blob字段类型,但是,在文件读写过程中,可能会造成数据表或者二进制文件损坏的情况。

    因此,更加实用的方案是:将文件上传到Tomcat服务器所在主机或者远程主机的硬盘上存储,而将文件上传记录(即:文件链接/地址)以字符串形似存储到数据库中;等到用户通过浏览器页面,需要浏览文件时,再通过数据库中记录的文件链接信息,从本地或者远程主机的硬盘读取文件,将其响应给浏览器客户端展示/下载。

在这里插入图片描述

基于Ajax+HTML的文件上传方案

文件上传-二进制数据

    在浏览器页面中实现文件选择功能,可以使用input type=file解决。
    需要上传的数据中,不建议携带除了上传资源以外的数据。但通常可以是,普通数据(用户名/id等)+二进制文件数据(二进制文件数据通过二进制流的方式发送给服务器)。

通过Ajax发送二进制流数据

    发送二进制流数据,仍然是通过FormData对象实现。即:

    ①将将要上传的数据存储到FormData对象中;
    ② 将processData属性的值设置为false,告诉浏览器发送对象请求数据
    ③ 将contentType属性的值设置为false,设置请求数据的类型为二进制类型。
    ④ 正常发送ajax请求即可

前端代码编写

    为了方便演示,通过JSP编写网页代码,举例如下:

<%--
  Created by IntelliJ IDEA.
  User: 13241
  Date: 2022/3/1
  Time: 20:52
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>主页</title>
</head>
<body>
    文件上传demo
    <form enctype="multipart/form-data">
            <%--普通数据--%>
        <p><label for="user">用户:</label><input id="user" type="text" name="username"/></p>
            <%--二进制文件数据--%>
        <p><label for="file">文件:</label><input id="file" type="file" name="file"/> <br>
            <a id="uploadFile" href="javascript:void(0)" onclick="upload()">立即上传</a></p>
        <p><input id="btnsubmit" type="submit" value="提交"/></p>
    </form>
    <script type="text/javascript" src="./js/jquery-3.6.0.min.js"></script>
    <script type="text/javascript">
        function upload(){
            //获取要上传的文件
            var file=document.getElementById("file").files[0];//二进制文件数据
            var username=document.getElementById("user").value;//普通数据
            if (file==undefined){
                alert("请先选择文件.")
                return false;
            }
            //将文件放入formData对象中
            var formData=new FormData();
            formData.append("file",file);
            formData.append("username",username);
            //ajax发送数据
            $.ajax({
                headers: {
                    //有需要的话-添加token
                    'token':"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NDcyNzM0MTEsImVtYWlsIjoiMTMyNDE1NjkyMEBxcS5jb20ifQ.EQYaVAMMqumGLKid_5EPhdz2VBJQBTbGFSDfdBYPwlM",
                },
                url:"/stasys_v3/upload/file",
                type:"POST",
                data:formData,
                processData:false,
                contentType:false,
                success:function (result) {
                    console.log(result)
                }
            })
        }

    </script>
</body>
</html>

文件上传-后端处理

    前端通过Ajax,借助FormData对象上传二进制流文件数据,在后端的业务逻辑处理,需要借助commons-fileuploadcommons-io开发包实现。

 <!--文件上传依赖-->
    <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>

    在编写代码时,需要在SpringMVC配置文件中,配置文件上传解析组件,具体配置如下:

  <!--文件上传解析组件
    id必须为multipartResolver
    springmvc默认使用该id找该组件
    -->
    <bean
	    id="multipartResolver"
	    class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
	      <!--单个文件的最大大小(以字节为单位)-1024*1024*512=512M-->
        <property name="maxUploadSizePerFile" value="536870912"/>
    </bean>

    后端处理后,可以将文件存储的链接/地址包含在json对象中响应给浏览器前端。例如:

{state:true,msg:“服务器繁忙”,url:”上传成功的资源的请求地址”}

后端代码编写

package com.xwd.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Controller
@RequestMapping(value = "/upload")
public class UploadController {

    //methods
    @RequestMapping(value = "/file")
    @ResponseBody
    public Map<String,Object> uploadFile(@RequestParam(value = "file") MultipartFile file,
                                         @RequestParam(value = "username") String username,
                                         HttpServletRequest request){
        System.out.println(file);
        System.out.println("username="+username);
        //获取protocol通信协议
        String scheme = request.getScheme();// http
        //获取服务名称
        String serverName = request.getServerName();//localhost
        //获取端口号
        int serverPort = request.getServerPort();//8010
        //contextPath
        String contextPath = request.getContextPath();// /stasys_v3
        String url=scheme+"://"+serverName+":"+serverPort+contextPath+"/upload/";
        Map<String,Object> map=new HashMap<>();
        // 控制文件大小
        if(file.getSize()>1024*1024*512){
            map.put("status",false);
            map.put("message", "文件大小不能超过512M");
            map.put("url",null);
            return map;
        }
        try {
            //保存文件到当前项目根目录下
            String realPath = request.getServletContext().getRealPath("/upload");
            File dir=new File(realPath);
            //判断目录是否存在
            if (!dir.exists()){
                dir.mkdirs();//不存在-创建新目录
            }
            //获取文件名
            String originalFilename = file.getOriginalFilename();
            //使用UUID替换文件名——避免文件名冲突
            String uuid= UUID.randomUUID().toString();
            //获取文件拓展名
            String extendsName = originalFilename.substring(originalFilename.lastIndexOf("."));
            //  控制文件类型
            if(!extendsName.equals(".zip")){
                map.put("status",false);
                map.put("message", "文件类型错误,必须为.zip压缩文件");
                map.put("url",null);
                return map;
            }
            //新的文件名-056af4c8-2a5a-4e6e-a108-45f689865264.zip
            String newFileName=uuid.concat(extendsName);
            //文件对应URL路径:http://localhost:8010/stasys_v3/upload056af4c8-2a5a-4e6e-a108-45f689865264.zip
            String fileUrl = url.concat(newFileName);
            //文件保存位置
            File saveLoc = new File(dir,newFileName);
            //保存文件
            file.transferTo(saveLoc);
            //填充返回值
            map.put("status",true);
            map.put("msg","文件上传成功!");
            map.put("url","");
        }catch (Exception e){
            e.printStackTrace();
            map.put("status",false);
            map.put("msg","文件上传失败!");
            map.put("url",fileUrl);
        }
        System.out.println(map.toString());
        return map;
    }
}


消息响应

在这里插入图片描述

文件服务器-分服务器文件上传

后端开发-服务器职责

在这里插入图片描述
    常见的服务器类型,例如:
    数据库服务器:运行数据库,提供基本属性数据的读写服务。
    缓存和消息服务器:负责处理高并发访问的数据缓存和消息
    文件服务器:负责存储用户上传的文件的服务器。
    应用服务器:用于部署JavaWeb应用。
    在实际开发中,会有很多职责不同的服务器,通过合理分工,可以提升项目的运行效率。

将Tomcat服务器作为文件服务器

制作并启动文件服务器

在这里插入图片描述

    可单独解压一个Tomcat,作为文件服务器。
    【0】创建文件保存文件夹:upload
在这里插入图片描述

    【1】配置文件服务器Server端口号:8006

<!-- Note:  A "Server" is not itself a "Container", so you may not
     define subcomponents such as "Valves" at this level.
     Documentation at /docs/config/server.html
 -->
<Server port="8006" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />

    【2】修改连接器端口号为:8090

    <!-- A "Connector" represents an endpoint by which requests are received
         and responses are returned. Documentation at :
         Java HTTP Connector: /docs/config/http.html
         Java AJP  Connector: /docs/config/ajp.html
         APR (HTTP/AJP) Connector: /docs/apr.html
         Define a non-SSL/TLS HTTP/1.1 Connector on port 8080
    -->
    <Connector port="8090" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

    【3】设置远程服务器非只读,修改web.xml中servlet初始化参数readOnly为false.

        <init-param>
            <param-name>readonly</param-name>
            <param-value>false</param-value>
        </init-param>

    【4】启动文件服务器
在这里插入图片描述

SSM项目:添加跨服务文件上传依赖

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

跨服务器文件上传:前端代码编写

    前端代码无需改动。

跨服务器文件上传:后端代码编写

    后端代码改动如下。

package com.xwd.controller;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @ClassName UploadController
 * @Description: com.xwd.controller
 * @Auther: xiwd
 * @Date: 2022/3/14 - 03 - 14 - 22:56
 * @version: 1.0
 */
@Controller
@RequestMapping(value = "/upload")
public class UploadController {
    //properties
    private final static String FileServerDir="http://192.168.0.104:8090/upload/";

    //setter

    //getter

    //constructors

    //methods
    @RequestMapping(value = "/file")
    @ResponseBody
    public Map<String,Object> uploadFile(@RequestParam(value = "file") MultipartFile file,
                                         @RequestParam(value = "username") String username,
                                         HttpServletRequest request){
        System.out.println(file);
        System.out.println("username="+username);
        //获取protocol通信协议
        String scheme = request.getScheme();// http
        //获取服务名称
        String serverName = request.getServerName();//localhost
        //获取端口号
        int serverPort = request.getServerPort();//8010
        //contextPath
        String contextPath = request.getContextPath();// /stasys_v3
        String url=scheme+"://"+serverName+":"+serverPort+contextPath+"/upload/";
        Map<String,Object> map=new HashMap<>();
        // 控制文件大小
        if(file.getSize()>1024*1024*512){
            map.put("status",false);
            map.put("message", "文件大小不能超过512M");
            map.put("url",null);
            return map;
        }
        try {
            //保存文件到当前项目根目录下
            String realPath = request.getServletContext().getRealPath("/upload");
            File dir=new File(realPath);
            //判断目录是否存在
            if (!dir.exists()){
                dir.mkdirs();//不存在-创建新目录
            }
            //获取文件名
            String originalFilename = file.getOriginalFilename();
            //使用UUID替换文件名——避免文件名冲突
            String uuid= UUID.randomUUID().toString();
            //获取文件拓展名
            String extendsName = originalFilename.substring(originalFilename.lastIndexOf("."));
            //  控制文件类型
            if(!extendsName.equals(".zip")){
                map.put("status",false);
                map.put("message", "文件类型错误,必须为.zip压缩文件");
                map.put("url",null);
                return map;
            }
            //新的文件名-056af4c8-2a5a-4e6e-a108-45f689865264.zip
            String newFileName=uuid.concat(extendsName);
            //创建sun公司提供的jersey包中的com.sun.jersey.api.client.Client类的对象
            Client client = Client.create();
            //提供文件服务器存储路径-http://192.168.0.104:8090/+新文件名
            WebResource webResource = client.resource(FileServerDir+newFileName);
            //提供文件的二进制数据file.getBytes()——保存文件到另一个服务器
             webResource.put(String.class, file.getBytes());
            //填充返回值
            map.put("status",true);
            map.put("msg","文件上传成功!");
            map.put("url",FileServerDir+newFileName);
        }catch (Exception e){
            e.printStackTrace();
            map.put("status",false);
            map.put("msg","文件上传失败!");
            map.put("url",null);
        }
        System.out.println(map.toString());
        return map;
    }
}

在这里插入图片描述

补充:基于Axios+Element UI文件上传方案

后端代码

    后端代码同改动之后的跨服务器文件上传代码一致。

Element UI组件使用与Axios数据请求

el-upload上传组件

    使用Element UI的el-upload上传组件。

			 <el-form-item label="实证文件:">
                <el-upload
                  class="upload-demo"
                  :file-list="form.filelist"
                  :show-file-list="true"
                  drag
                  action="http://www.baidu.com"
                  :before-upload="beforeUpload"
                  :on-change="addFile"
                  :on-success="uploadSuccess"
                  :multiple="false"
                  :auto-upload="false"
                  accept=".zip"
                  :limit="1"
                >
                  <i class="el-icon-upload"></i>
                  <div class="el-upload__text">
                    将文件拖到此处,或<em>点击上传</em>
                  </div>
                  <div class="el-upload__tip" slot="tip">
                    只能上传单个zip压缩文件,重复上传会导致文件覆盖
                  </div>
                </el-upload>

el-upload事件处理

    Element UI的el-upload上传组件的事件处理函数,根据需要修改函数体代码即可。

	//文件上传之前文件类型检验
    beforeUpload(file) {
      console.log(file)
    },
    //添加文件-添加文件、上传成功和上传失败时都会被调用
    addFile(file, fileList) {
      //记录文件名称
      this.form.evidence = this.form.email + new Date().getTime() + file.name
      //记录文件对象
      this.form.file = file
      console.log(this.form.file)
      console.log(this.form.evidence)
    },
    //文件上传成功
    uploadSuccess(response, file, fileList) {
      console.log(response)
      console.log(file)
      console.log(fileList)
    },

Axios发送文件上传请求

    使用Axios发送文件上传请求。

axios({ 
          headers: { 'Content-Type': 'multipart/form-data' }, 
          url: 'http://localhost:8010/stasys_v3/upload/file', 
          method: 'POST', 
          data: formData, 
        }) 
          .then((res) => { 
            console.log(res) 
            if (res.data && res.data.status) { 
              //文件已上传成功
              this.$message({ 
                message: '文件已上传!', 
                type: 'success', 
              }) 
            } else { 
              this.$message.error('文件上传失败!') 
            } 
          }) 
          .catch((error) => { 
            console.log(error) 
            this.$message.error('信息上报失败!') 
          })

    响应结果如下:

msg: "文件上传成功!"
​status: true
​url: "http://192.168.0.104:8090/upload/ca71cdb0-9245-4091-a623-1b1be4fa5ed3.zip"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是席木木啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值