spring-boot实现静态资源代理

496 阅读2分钟

项目背景

某项目背景下,希望前后端开发分离,运行时前后端需共用端口,需要注意的是:要求在nginx反向代理之前就实现共用端口,所以最简单的方案就是采用spring-boot 路由来编码实现代理,这里只论证了技术点,实际项目改造需要进一步优化

要点:

  1. 正确的设置静态文件目录,这里只是抛砖引玉,实际项目中请动态获取,或者根据配置获取
  2. 无论是html文件,还是静态文件,都需要获取到准确的minetype ,通过sdk提供的基本api是做不到的,所以我们采用了第三方库Tika,只有准确的描述ContentType,浏览器才能正确的加载静态资源
  3. 采用了持续输出流的方式写入响应结果,确保不会因为文件过大导致的内存溢出问题

具体代码如下:

package com.yyht.basic.mgt.controller;

import org.springframework.core.io.FileSystemResource;
import cn.kawins.mybatis.base.BaseController;
import com.yyht.basic.mgt.model.InstalledContent;
import io.swagger.annotations.Api;
import org.apache.tika.Tika;
import org.apache.tika.mime.MediaType;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 静态资源代理服务
 *
 * @author dangcc
 */
@RestController
@RequestMapping("")
@Api(tags = "静态资源代理")
public class TestController extends BaseController<InstalledContent> {
    private final Path staticDir = Paths.get("D:\workspace\web\dist\");

    @GetMapping(value = "/html/**")
    public void getFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String filepath = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
        Pattern pattern = Pattern.compile(".*/html/(.*)");
        Matcher matcher = pattern.matcher(filepath);
        if (matcher.matches()) {
            filepath= matcher.group(1);
        }
        if (StringUtils.isEmpty(filepath)) {
            filepath = "index.html";
        }
        
        Path file = staticDir.resolve(filepath);
        //检测文件是否存在
        if (!Files.exists(file)) {
          //返回404的状态
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }


        FileSystemResource resource = new FileSystemResource(file);
        if (!resource.exists() || !resource.isReadable()) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        //采取获取mimetype第三方库 这个很重要,因为mime对了浏览器才会正常加载文件流,否则会拒绝载入
        //gradle 安装插件 implementation 'org.apache.tika:tika-core:1.27'
        Tika tika = new Tika();
        MediaType mediaType = MediaType.parse(tika.detect(file));
        response.setContentType(mediaType.toString());
        response.setContentLength((int) Files.size(file));

        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = resource.getInputStream();
            outputStream = response.getOutputStream();
            byte[] buffer = new byte[1024];
            int bytesRead;
            //输出流
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            outputStream.flush();
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
            if (outputStream != null) {
                outputStream.close();
            }
        }
    }
}