【SpringBoot】如何优雅的实现跨服务器上传文件

项目完整代码链接:代码链接

文章目录

  • 一、创建项目
    • 1.1 上传文件的项目
    • 1.2 创建fileuploadserver 保存文件服务器
  • 二、编写服务器接收文件上传代码
  • 三、分析存在问题以及解决办法
    • 3.1 问题分析
    • 3.2 修改项目fileupload的配置
    • 3.3 创建fileuploadserver1 项目
    • 3.4 编写服务器接收文件上传代码
    • 3.5 编写文件服务器接收代码
    • 3.6 效果展示

跨服务上传文件示意图
【SpringBoot】如何优雅的实现跨服务器上传文件_第1张图片

一、创建项目

  • springboot:2.2.6
  • JDK:1.8

由于资源有限,就用不同端口表示不同服务器了。

1.1 上传文件的项目

首先idea快速搭建工具创建一个springboot项目,名字为fileupload,作为上传文件的服务端。

选择spring web模块即可

【SpringBoot】如何优雅的实现跨服务器上传文件_第2张图片
配置相关参数

spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=30MB
spring.servlet.multipart.max-request-size=30MB
#文件保存的url,末尾的 / 别漏了
file.upload.path=http://localhost:8888/fileuploadserver/uploads/

添加坐标依赖

跨服器上传所需要的jar包坐标

<dependency>
    <groupId>com.sun.jerseygroupId>
    <artifactId>jersey-coreartifactId>
    <version>1.18.1version>
dependency>
<dependency>
    <groupId>com.sun.jerseygroupId>
    <artifactId>jersey-clientartifactId>
    <version>1.18.1version>
dependency>

1.2 创建fileuploadserver 保存文件服务器

创建一个jeex项目,项目名字为fileuploadserver,什么都不需要配置。然后再webapp目录下创建一个uploads文件夹,与上面项目设置的文件保存地址一直,然后配置好tomcat环境启动即可。记得把web.xml文件里面的配置信息删掉

如下图所示

【SpringBoot】如何优雅的实现跨服务器上传文件_第3张图片

【SpringBoot】如何优雅的实现跨服务器上传文件_第4张图片

【SpringBoot】如何优雅的实现跨服务器上传文件_第5张图片

记得改下Http和JMX的端口,免得和其他项目冲突了。

【SpringBoot】如何优雅的实现跨服务器上传文件_第6张图片

二、编写服务器接收文件上传代码

编写一个controller类,用与处理文件上传
MultipartFile类用来保存上传的文件数据

package cn.jxj4869.fileupload.controller;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import org.springframework.beans.factory.annotation.Value;
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 java.io.IOException;
import java.util.UUID;

@Controller

public class FileController {
    @Value("${file.upload.path}")
    private String path;


    @RequestMapping("/fileupload/method1")
    @ResponseBody
    private String method1(@RequestParam("upload") MultipartFile upload) throws IOException {
        System.out.println("跨服务器上传文件上传");

        String filename = upload.getOriginalFilename();
        // 把文件的名称设置唯一值,uuid
        String uuid = UUID.randomUUID().toString().replace("-", "");
        filename = uuid + "_" + filename;
        // 创建客户端的对象

        Client client = Client.create();

        // 和图片服务器进行连接
        WebResource webResource = client.resource(path + filename);

        webResource.put(upload.getBytes());
        return "success";
    }
}

前端代码

放在/resources/static/目录下


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>fileuploadtitle>
head>
<body>
<h3>method1h3>
<form method="post" enctype="multipart/form-data" action="fileupload/method1">
    <input type="file" name="upload">
    <br>
    <input type="submit">
form>
body>
html>

上传效果如下

【SpringBoot】如何优雅的实现跨服务器上传文件_第7张图片

【SpringBoot】如何优雅的实现跨服务器上传文件_第8张图片

【SpringBoot】如何优雅的实现跨服务器上传文件_第9张图片

【SpringBoot】如何优雅的实现跨服务器上传文件_第10张图片

三、分析存在问题以及解决办法

3.1 问题分析

正如上面写的,只要我们关联了服务器地址之后就可以直接通过put方法把文件上传上去,这无疑是非常危险的行为。因为在上传过程中并没有进行用户校验,那么如果被人知道了服务器保存图片的路径,甚至不需要知道准确路径,只要知道服务器ip地址就够了,那么他就可以通过put方法无限量的进行服务器上传。

根据apache官方在2017公布的一个漏洞,如果开启了put方法,那么就可以任意写写入文件到服务器。但是如果禁用了put方法,那么又有导致一些需要put方法的业务无法使用。

一个解决办法就是修改tomcat的配置。修改在tomcat的/conf目录下的web.xml。找到下面这段

readonly设置成true。这样就无法通过put往服务器中写入文件了。

    <servlet>
        <servlet-name>defaultservlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServletservlet-class>
        <init-param>
            <param-name>readonlyparam-name>
            <param-value>trueparam-value>
        init-param>
    servlet>

但是这样一来,我们就无法通过上述方法来进行跨服务器上传了,因为文件服务器已经禁止了通过put方法写入文件。那么这种情况应该怎么办呢?

有一种思路就是把服务器接收到的文件上传请求,通过HttpPost再把上传的文件信息发送到文件服务器。由文件服务器自己处理是否接收保存文件。

【SpringBoot】如何优雅的实现跨服务器上传文件_第11张图片

3.2 修改项目fileupload的配置

添加HttpPost的相关坐标依赖

        
            org.apache.httpcomponents
            httpclient
            4.3.6
        
        
            org.apache.httpcomponents
            httpmime
            4.5
        
        
            org.apache.httpcomponents
            httpcore
            4.4.1
        

添加配置:

application.properties

file.upload.path1=http://localhost:8888/fileupload/

3.3 创建fileuploadserver1 项目

创建一个springboot项目,选择如**fileload**项目一样。

【SpringBoot】如何优雅的实现跨服务器上传文件_第12张图片

创建好之后在/resources/目录下创建一个uploads文件夹,用作保存上传文件的位置。(也可以根据自己实际需要,更改文件保存的位置)

配置相关参数

# 文件上传位置  这里是路径是相对于项目而言,可以根据实际情况更改
file.upload.save-path=/uploads/
#文件访问路径
file.upload.url=/uploads/**

server.port=8888

#文件大小设置
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=30MB
spring.servlet.multipart.max-request-size=100MB

3.4 编写服务器接收文件上传代码

用数组的形式接收MultipartFile参数,实现多文件上传。

把上传的文件用MultipartEntityBuilder打包好之后,再用HttpPost发送到文件服务器。这里最好需要了解一些HttpPost用法。

package cn.jxj4869.fileupload.controller;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.beans.factory.annotation.Value;
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 java.io.IOException;
import java.util.UUID;

@Controller

public class FileController {

    @Value("${file.upload.path1}")
    private String path1;


    @RequestMapping("/fileupload/method2")
    @ResponseBody
    private String method2(@RequestParam("upload") MultipartFile[] uploads) throws IOException {
        System.out.println("跨服务器上传文件上传");
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(path1);
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        for (MultipartFile upload : uploads) {
            String filename = upload.getOriginalFilename();
            builder.addBinaryBody("upload", upload.getBytes(), ContentType.MULTIPART_FORM_DATA, filename);
        }
        try {

            HttpEntity entity = builder.build();
            httpPost.setEntity(entity);
            CloseableHttpResponse response = httpClient.execute(httpPost);
            System.out.println(response.getStatusLine().getStatusCode());
            String s = response.getEntity().toString();
            System.out.println(s);
        } catch (Exception e) {

        } finally {
            httpClient.close();
        }
        return "success";
    }
}

前端部分的代码


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>fileuploadtitle>
head>
<body>
<h3>method2h3>
<form method="post" enctype="multipart/form-data" action="fileupload/method2">
    <input type="file" name="upload"><br><br>
    <input type="file" name="upload"><br><br>
    <input type="file" name="upload">
    <br><br>
    <input type="submit">
form>
body>
html>

3.5 编写文件服务器接收代码

接收的Controller

ResourceUtils.getURL("classpath:") 获取当前项目所在的路径,最好别存在中文,可能会出错

package cn.jxj4869.fileuploadserver1.controller;

import com.sun.javafx.scene.shape.PathUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.stereotype.Controller;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.PostMapping;
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.io.IOException;

import java.util.UUID;

@Controller
public class FileController {

    @Value("${file.upload.save-path}")
    private String savePath;

    @PostMapping("/fileupload")
    @ResponseBody
    private String fileupload(HttpServletRequest request, @RequestParam("upload")MultipartFile[] uploads) throws IOException {
        System.out.println("文件上传");
        
        String path= ResourceUtils.getURL("classpath:").getPath()+savePath;

        File file = new File(path);
        if (!file.exists()) {
            file.mkdir();
        }
        for (MultipartFile upload : uploads) {
            String filename = upload.getOriginalFilename();
            String uuid = UUID.randomUUID().toString().replace("-", "");
            filename=uuid+"_"+filename;
            upload.transferTo(new File(path,filename));
        }
        return "success";

    }
}

编写配置类

package cn.jxj4869.fileuploadserver1.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cglib.core.WeakCacheKey;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MySpringMvcConfig implements WebMvcConfigurer {

    @Value("${file.upload.save-path}")
    private String savePath;
    @Value("${file.upload.url}")
    private String url;
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler(url).addResourceLocations("classpath:"+savePath);
    }
}

3.6 效果展示

【SpringBoot】如何优雅的实现跨服务器上传文件_第13张图片

【SpringBoot】如何优雅的实现跨服务器上传文件_第14张图片

【SpringBoot】如何优雅的实现跨服务器上传文件_第15张图片

如果对您有帮助,麻烦三连走一波

你可能感兴趣的:(SpringBoot,JavaWeb)