实现在线访问OSS中的PDF、图片等文件(OSS不支持在线访问,而是默认下载)

问题分析

当 OSS 返回的响应头中Content-Disposition设置为attachment时,浏览器会强制下载文件。要实现在线预览,需要确保:

  1. 响应头Content-Disposition为inline
  2. 正确设置Content-Type头(如text/html、text/css、application/javascript)
  3. 处理 HTML 中相对路径资源的引用问题

示例代码

// OssProxyController.java (适配OkHttp 3.6且支持PDF代理)
package com.example.demo.controller;

import okhttp3.*;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

@RestController
@RequestMapping("/api/proxy")
public class OssProxyController {

    private final OkHttpClient client = new OkHttpClient();

    // 代理HTML请求
    @GetMapping("/html")
    public void proxyHtml(@RequestParam("url") String ossUrl, 
                          HttpServletResponse response) throws IOException {
        proxyRequest(ossUrl, response, "text/html");
    }

    // 代理CSS请求
    @GetMapping("/css")
    public void proxyCss(@RequestParam("url") String ossUrl, 
                         HttpServletResponse response) throws IOException {
        proxyRequest(ossUrl, response, "text/css");
    }

    // 代理JS请求
    @GetMapping("/js")
    public void proxyJs(@RequestParam("url") String ossUrl, 
                        HttpServletResponse response) throws IOException {
        proxyRequest(ossUrl, response, "application/javascript");
    }

    // 代理PDF请求
    @GetMapping("/pdf")
    public void proxyPdf(@RequestParam("url") String ossUrl, 
                         HttpServletResponse response) throws IOException {
        proxyRequest(ossUrl, response, "application/pdf");
    }

    // 通用代理方法
    private void proxyRequest(String ossUrl, HttpServletResponse response, String contentType) 
            throws IOException {
        Request request = new Request.Builder()
               .url(ossUrl)
               .build();

        Response ossResponse = null;
        InputStream inputStream = null;
        OutputStream outputStream = null;

        try {
            ossResponse = client.newCall(request).execute();
            if (!ossResponse.isSuccessful()) {
                response.sendError(ossResponse.code(), "OSS请求失败");
                return;
            }

            // 设置响应头
            response.setContentType(contentType);
            response.setHeader("Content-Disposition", "inline");

            // 获取响应体流
            ResponseBody body = ossResponse.body();
            if (body!= null) {
                inputStream = body.byteStream();
                outputStream = response.getOutputStream();

                // 复制流内容
                byte[] buffer = new byte[4096];
                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();
            if (ossResponse!= null) ossResponse.body().close();
        }
    }
}

使用方法

  1. 部署上述代码到你的Spring Boot应用中。
  2. 当你想要预览PDF文件时,通过如下方式请求接口:
GET http://your-server/api/proxy/pdf?url=https://your-bucket.oss-cn-hangzhou.aliyuncs.com/your-pdf-file.pdf

通过这个接口代理请求,会修改响应头,将Content-Disposition设置为inline,使得浏览器尝试在线预览而不是自动下载,同时设置正确的Content-Type(针对PDF为application/pdf),并将从OSS获取到的文件内容流转发给浏览器,实现在线预览的效果。

需要注意的是,对于大的PDF文件,这种简单的流复制方式可能在性能上有一定局限。

示例2:代理 HTML 文件并动态替换其中的资源路径,使其指向正确的代理 URL

我理解您的需求是要代理HTML文件并动态替换其中的资源路径,使其指向正确的代理URL。这是一个常见的前端资源代理问题。

解决方案

修改HTML代理方法:

  1. 下载HTML内容
  2. 解析并替换其中的资源引用
  3. 将修改后的内容返回给客户端

改进后的代码实现:

// OssProxyController.java (增强版HTML代理)

// 代理HTML请求 - 增强版
@GetMapping("/html")
public void proxyHtml(@RequestParam("url") String ossUrl,
                      HttpServletResponse response) throws IOException {
    // 解析基础URL,用于替换相对路径
    String baseUrl = extractBaseUrl(ossUrl);
    
    // 获取HTML内容并替换资源路径
    String modifiedHtml = fetchAndModifyHtml(ossUrl, baseUrl);
    
    // 设置响应头
    response.setContentType("text/html; charset=utf-8");
    response.setHeader("Content-Disposition", "inline");
    
    // 返回修改后的HTML
    response.getWriter().write(modifiedHtml);
}

// 提取基础URL (例如: https://your-bucket.oss-cn-hangzhou.aliyuncs.com/path/to/)
private String extractBaseUrl(String fullUrl) {
    int lastSlashIndex = fullUrl.lastIndexOf('/');
    if (lastSlashIndex > 0) {
        return fullUrl.substring(0, lastSlashIndex + 1);
    }
    return fullUrl;
}

// 获取HTML内容并修改资源路径
private String fetchAndModifyHtml(String ossUrl, String baseUrl) throws IOException {
    Request request = new Request.Builder()
           .url(ossUrl)
           .build();
    
    try (Response ossResponse = client.newCall(request).execute()) {
        if (!ossResponse.isSuccessful()) {
            throw new IOException("OSS请求失败: " + ossResponse.code());
        }
        
        // 读取HTML内容
        String htmlContent = ossResponse.body().string();
        
        // 替换资源路径
        return replaceResourcePaths(htmlContent, baseUrl);
    }
}

// 替换HTML中的资源路径
private String replaceResourcePaths(String htmlContent, String baseUrl) {
    // 替换JS路径
    htmlContent = htmlContent.replaceAll("src=\"js/", "src=\"/api/proxy/js?url=" + baseUrl + "js/");
    htmlContent = htmlContent.replaceAll("src=\"./js/", "src=\"/api/proxy/js?url=" + baseUrl + "js/");
    
    // 替换CSS路径
    htmlContent = htmlContent.replaceAll("href=\"css/", "href=\"/api/proxy/css?url=" + baseUrl + "css/");
    htmlContent = htmlContent.replaceAll("href=\"./css/", "href=\"/api/proxy/css?url=" + baseUrl + "css/");
    
    // 替换图片路径
    htmlContent = htmlContent.replaceAll("src=\"images/", "src=\"/api/proxy/auto?url=" + baseUrl + "images/");
    htmlContent = htmlContent.replaceAll("src=\"./images/", "src=\"/api/proxy/auto?url=" + baseUrl + "images/");
    
    // 替换其他相对路径资源
    htmlContent = htmlContent.replaceAll("src=\"/", "src=\"/api/proxy/auto?url=" + baseUrl);
    htmlContent = htmlContent.replaceAll("href=\"/", "href=\"/api/proxy/auto?url=" + baseUrl);
    
    return htmlContent;
}
功能说明

改进如下:

  1. 提取基础URL

    • 从原始URL中提取基础路径(如:https://your-bucket.oss-cn-hangzhou.aliyuncs.com/path/to/)
    • 用于构建完整的资源URL
  2. 路径替换

    • 处理相对路径:src="js/script.js"src="/api/proxy/js?url=https://your-bucket.oss-cn-hangzhou.aliyuncs.com/path/to/js/script.js"
    • 处理当前目录相对路径:src="./js/script.js"src="/api/proxy/js?url=https://your-bucket.oss-cn-hangzhou.aliyuncs.com/path/to/js/script.js"
    • 同时处理CSS、图片等其他资源类型
  3. 通用资源代理

    • 使用/api/proxy/auto接口自动识别资源类型
    • 无需为每种资源类型单独创建替换规则
使用示例

URL:

https://huarunbaizun-test.obs.cn-east-3.myhuaweicloud.com/percen-smart/productUi/0fad1362-21bd-4d5d-a0f5-21518dc4eb63/index.html

代理请求将是:

http://your-server/api/proxy/html?url=https://huarunbaizun-test.obs.cn-east-3.myhuaweicloud.com/percen-smart/productUi/0fad1362-21bd-4d5d-a0f5-21518dc4eb63/index.html

当HTML被下载后,其中的资源引用(如src="js/main.js")会被自动替换为指向您服务端的代理URL,确保所有资源都能正确加载。

注意事项
  1. 这种正则表达式替换方法适用于大多数情况,但对于复杂的HTML结构可能不够健壮。在生产环境中,考虑使用HTML解析库(如Jsoup)进行更精确的处理。

  2. 如果HTML中包含内联JavaScript或CSS,可能会误替换其中的字符串。可以通过更复杂的正则表达式或解析器来避免这种情况。

  3. 对于大型HTML文件,这种处理方式可能会增加响应时间。考虑实现缓存机制来提高性能。

你可能感兴趣的:(实现在线访问OSS中的PDF、图片等文件(OSS不支持在线访问,而是默认下载))