问题分析
当 OSS 返回的响应头中Content-Disposition设置为attachment时,浏览器会强制下载文件。要实现在线预览,需要确保:
- 响应头Content-Disposition为inline
- 正确设置Content-Type头(如text/html、text/css、application/javascript)
- 处理 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();
}
}
}
使用方法
- 部署上述代码到你的Spring Boot应用中。
- 当你想要预览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代理方法:
- 下载HTML内容
- 解析并替换其中的资源引用
- 将修改后的内容返回给客户端
改进后的代码实现:
// 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;
}
功能说明
改进如下:
-
提取基础URL:
- 从原始URL中提取基础路径(如:https://your-bucket.oss-cn-hangzhou.aliyuncs.com/path/to/)
- 用于构建完整的资源URL
-
路径替换:
- 处理相对路径:
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、图片等其他资源类型
- 处理相对路径:
-
通用资源代理:
- 使用
/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,确保所有资源都能正确加载。
注意事项
-
这种正则表达式替换方法适用于大多数情况,但对于复杂的HTML结构可能不够健壮。在生产环境中,考虑使用HTML解析库(如Jsoup)进行更精确的处理。
-
如果HTML中包含内联JavaScript或CSS,可能会误替换其中的字符串。可以通过更复杂的正则表达式或解析器来避免这种情况。
-
对于大型HTML文件,这种处理方式可能会增加响应时间。考虑实现缓存机制来提高性能。