实现在线访问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上的PDF文件上传和下载操作,需先完成以下准备工作: - 安装并配置好Java开发环境(适用于采用Java SDK的情况),对于其他编程语言则安装对应版本的SDK。 - 注册成为阿里云用户,并创建存储空间(Bucket),获取AccessKeyId以及AccessKeySecret。 #### 二、PDF文件上传至阿里云OSS 利用阿里云官方提供的多种语言版SDK可以轻松地将PDF文档上传到指定Bucket内。以下是基于Python语言的一个简单实例[^2]: ```python import os from aliyunsdkcore.client import AcsClient from aliyunsdkoss.request.v20190517.PutObjectRequest import PutObjectRequest def upload_pdf_to_oss(file_path, bucket_name='your-bucket-name'): client = AcsClient('<Your Access Key Id>', '<Your Access Key Secret>', 'cn-hangzhou') request = PutObjectRequest(bucket_name=bucket_name) file_name = os.path.basename(file_path) with open(file_path, "rb") as f: content = f.read() response = client.do_action_with_exception(request.set_content(content).set_key(file_name)) print(f'Upload {file_name} successfully.') ``` 此段代码实现了从本地路径读取PDF文件并通过API接口将其放置于设定的目标Bucket之中。 #### 三、从阿里云OSS下载PDF文件 同样借助阿里云提供的SDK功能可以从云端获取之前上传过的PDF资源。这里给出一段用于下载文件的例子: ```python from aliyunsdkcore.client import AcsClient from aliyunsdkoss.request.v20190517.GetObjectRequest import GetObjectRequest def download_pdf_from_oss(object_name, save_as, bucket_name='your-bucket-name'): client = AcsClient('<Your Access Key Id>', '<Your Access Key Secret>', 'cn-hangzhou') get_request = GetObjectRequest(bucket_name=bucket_name, key=object_name) result = client.do_action_with_exception(get_request) with open(save_as, 'wb') as fp: fp.write(result) print('Download completed!') ``` 上述函数接收三个参数:`object_name`(即要下载的对象名称), `save_as`(保存后的文件) 和可选参数bucket的名字,默认为'your-bucket-name'. 执行后会把对应的PDF文件保存到当前工作目录下。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值