文件上传之跨服务器上传文件

在实际项目中上传文件保存到到文件服务器是一个常用的操作,而在服务器上保存文件就需要特别小心。因为通常情况下不只是在一个路径里保存文件,所以需要实践一下保存文件到任意位置。当然,前提是你的应用程序有这样的操作权限。

一、分布式服务器上传作用

  • 数据库服务器:运行我们的数据库
  • 缓存和消息服务器:负责处理大并发访问的缓存和消息
  • 文件服务器:负责存储用户上传文件的服务器。
  • 应用服务器:负责部署我们的应用
  • 在实际开发中,我们会有很多处理不同功能的服务器。(注意:此处说的不是服务器集群)
  • 总结:分服务器处理的目的是让服务器各司其职,从而提高我们项目的运行效率。

分布式服务器工作示意图:

二、单独解压一个Tomcat作为文件服务器

除了项目之外,单独在准备一个Tomcat,如果和项目在同一个电脑上则需要对第二个Tomcat做如下设置

  • server.xml中需要修改三个端口,不然会出现端口冲突

 

  • 在web.xml中添加如下内容 :远程服务器中设置非只读
<init-param>
    <param-name>readonly</param-name>
    <param-value>false</param-value>
</init-param>

 注意:我这里用的是虚拟机中的Tomcat,所以只需要设置非只读即可

webapps下创建一个upload目录,用于存放上传的图片

启动测试

 

三、导入跨服务器上传文件依赖

这里还是在上一节文件上传环境上改动而来,在pom.xml中添加依赖

<!-- https://mvnrepository.com/artifact/com.sun.jersey/jersey-client -->
    <dependency>
      <groupId>com.sun.jersey</groupId>
      <artifactId>jersey-client</artifactId>
      <version>1.19.4</version>
    </dependency>

四、创建数据库

创建数据库存储上传的信息

CREATE TABLE `player` (
  `id` int(25) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `nickname` varchar(255) DEFAULT NULL,
  `photo` varchar(255) DEFAULT NULL,
  `filetype` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
)

结果如下:

五、在pojo包下创建player表的实体类

package com.augus.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class Player implements Serializable {
    private Integer id;
    private String username;
    private String password;
    private String nickname;
    private String photo;
    private String filetype;
}

六、在WEB-INF/templates下创建userinformation.html,user.html

  • userinformation.html:实现数据提交和图片上传
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>userinformation</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 src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js" type="text/javascript"></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();
                //注意这里写的headPhoto,在controller中也要用这个名字
                formData.append("headPhoto",photoFile)
                // ajax向后台发送文件
                $.ajax({
                    type:"post",
                    data:formData,
                    url:"fileUpload.do",
                    processData:false,
                    contentType:false,
                    // result能获取到后台返回的信息
                    success:function(result){
                        // 接收后台响应的信息,返回message键所对应的值
                        alert(result.message)
                        //设置图片回显
                        $("#headImg").attr("src","http://192.168.141.134:8080/upload/"+result.fileName)
                        //将文件类型和文件名放入form表单
                        //就是设置input标签value属性的值
                        $("#photoId").val(result.fileName)
                        $("#filetypeId").val(result.fileType)
                    },
                    //控制进度条进度
                    xhr: function() {
                        var xhr = new XMLHttpRequest();
                        //使用XMLHttpRequest.upload监听上传过程,注册progress事件,打印回调函数中的event事件
                        xhr.upload.addEventListener('progress', function (e) {
                            console.log(e);
                            //loaded代表上传了多少
                            //total代表总数为多少
                            var progressRate = (e.loaded / e.total) * 100 + '%';

                            //通过设置进度条的宽度达到效果
                            $('.progress > div').css('width', progressRate);
                        })

                        return xhr;
                    }
                })
            })
        })
    </script>
</head>
<body>
<form action="addUser" method="post">
    <table style="margin: auto;">
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="username" required><br></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="text" name="password" required><br></td>
        </tr>
        <tr>
            <td>昵称:</td>
            <td><input type="text" name="nickname" required><br></td>
        </tr>
        <tr>
            <td>头像:</td>
            <td><input id="photo" type="file"><br></td>
            <td>
                <div class="progress">
                    <div></div>
                </div>
            </td>
            <td><a id="uploadFile" href="javascript:void(0)">立即上传</a><br></td>
            <!--使用隐藏的输入框存储文件名称和文件类型-->
            <td><input id="photoId" type="hidden" name="photo"></td>
            <td><input id="filetypeId" type="hidden" name="filetype"></td>
        </tr>
        <tr>
            <td colspan="2"><img id="headImg" style="width: 200px;height: 200px" alt="还未上传图片"><br></td>
        </tr>
        <tr>
            <td colspan="2">
                <input type="submit" value="提交" style="margin: auto;">
            </td>
        </tr>
    </table>
</form>
</body>
</html>
  • user.html:实现注册成功后的跳转页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册后页面</title>
</head>
<body>
<h1>注册成功</h1>
</body>
</html>

七、在controller下创建FileUploadController,内容如下

package com.augus.controller;


import com.augus.pojo.Player;
import com.augus.service.PlayerService;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Controller
public class FileUploadController {
    //文件存储位置,定为私有不可修改的静态属性
    private final static String FILESERVER = "http://192.168.141.134:8080/upload/";

    @Autowired
    private PlayerService playerService;

    //打开信息提交页面
    @RequestMapping("/regist")
    public String testRegister(){
        return "userinformation";
    }

    @ResponseBody
    @RequestMapping("/fileUpload.do")
    public Map<String,String> fileUpload(MultipartFile headPhoto, HttpServletRequest request) throws IOException {
        //创建Map集合
        Map<String, String> map = new HashMap<String, String>();

        //控制文件大小,如果文件大小超过5m,就给前端返回提示
        //还有一种直接可以在springmvcxml中即可控制,但是不推荐,没有办法控制给前端的提示信息
        if(headPhoto.getSize()>1024*1024*5){
            map.put("message","文件大小不能超过5M");
            return map;
        }

        // 获取文件名,这里获取的是文件名,但是如果有文件名重复,则会出现覆盖,这时候在保存原文件的时候,就不同用原名字
        String originalFilename = headPhoto.getOriginalFilename();

        //那么就用UUID替换文件名
        String s = UUID.randomUUID().toString();

        //从获取上传进来的文件后缀名 例如 4.jpg 就取.jpg
        //lastIndexOf(".")截取文件中最后一个点,防止有多个.
        String extendsName = originalFilename.substring(originalFilename.lastIndexOf("."));

        //控制文件类型要是.jpg格式,如果不是则需要给前端返回提示信息
        if(!extendsName.equals(".jpg")){
            //指定返回值
            map.put("message", "图片类型必须是.jpg");
            return map;
        }

        // 使用 UUID+后缀名,拼接成文件名
        String newFileName = s + extendsName;

        //创建sun公司提供的jersey包中的client对象
        Client client = Client.create();
        //将存储地址和文件名组合
        WebResource resource = client.resource(FILESERVER+newFileName);
        //文件保存到另一个服务器, 将上传的图片以新名字提交的另一个图片服务器
        resource.put(String.class, headPhoto.getBytes());

        System.out.println("这是fileUpload");
        //设置上传成功时的响应
        map.put("message","上传成功");
        map.put("fileName",newFileName);
        map.put("fileType",headPhoto.getContentType()); //返回文件类型
        return map;
    }

    @RequestMapping("/addUser")
    public String testAddUser(Player player){
        playerService.addUser(player);
        return "redirect:/user";
    }
}

八、在service层处理如下

  • 在service下创建PlayerService
package com.augus.service;

import com.augus.pojo.Player;

public interface PlayerService {
   int addUser(Player player);
}
  • 在service下impl包下创建PlayerServiceImpl
package com.augus.service.impl;

import com.augus.mapper.PlayerMapper;
import com.augus.pojo.Player;
import com.augus.service.PlayerService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class PlayerServiceImpl implements PlayerService {
    //这个本来@Autowired保存,所以改用@Resource
    @Resource
    private PlayerMapper playerMapper;

    @Override
    public int addUser(Player player) {
        return playerMapper.addUser(player);
    }
}

九、在mapper层处理如下

  • 创建接口PlayerMapper
package com.augus.mapper;

import com.augus.pojo.Player;

public interface PlayerMapper {
    int addUser(Player player);
}
  • 创建PlayerMapper.xml,操作数据库
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.augus.mapper.PlayerMapper">
    <insert id="addUser">
        insert into player values(DEFAULT ,#{username},#{password},#{nickname},#{photo},#{filetype})
    </insert>
</mapper>

十、重新部署Tomcat,访问http://localhost:8080/upfile_war_exploded/regist

 

 

 

posted @ 2019-10-28 10:12  酒剑仙*  阅读(701)  评论(0编辑  收藏  举报