在 Oracle APEX 中使用 OCI 对象存储显示图片
OCI 对象存储Oracle APEXPL/SQL图片处理安全访问缓存策略
原文地址: https://blogs.ontoorsolutions.com/post/displaying-images-in-oracle-apex-using-oracle-cloud-infrastructure-oci-object-storage/
# 在 Oracle APEX 中使用 OCI 对象存储显示图片
在现代 Oracle APEX 应用中,将图片存储在数据库表内通常不是最佳选择。OCI 对象存储提供更好的扩展性、性能和成本效益。
## 为什么使用 OCI 对象存储存储图片
OCI 对象存储是存储图片的理想选择,因为:
- 不会增加数据库大小
- 专为大型二进制文件设计
- 默认具备安全性
- 与 APEX 和 ORDS 无缝协作
- 支持通过 PAR 实现时间限制的访问
默认情况下,OCI 不允许公开访问,这很好。它强制要求明确指定图片的暴露方式。

## 在 APEX 中显示 OCI 图片的两种安全方法
只有两种生产环境安全的方法:
1. **APEX 代理(安全)**
浏览器 → APEX → OCI 对象存储
使用凭据由 APEX 获取图片
流式传输到浏览器
完全会话控制
2. **预认证请求(PAR)(快速)**
浏览器 → OCI 对象存储
临时公开 URL
无 APEX 负载
最适合画廊和缩略图
我们将从 PAR 开始介绍这两种方法。
## 完整的 OCI PAR 生成代码(PL/SQL)
此函数生成可用于浏览器单个对象的 PAR URL。
### 功能
- 创建对象级 PAR
- 设置过期时间(小时)
- 返回可在 `<img src>` 中使用的直接 URL
### PL/SQL 函数
CREATE OR REPLACE FUNCTION get_object_par_url (
in_region IN VARCHAR2, -- ap-mumbai-1
in_namespace IN VARCHAR2, -- OCI namespace
in_bucket_name IN VARCHAR2, -- bucket name
in_object_name IN VARCHAR2, -- images/product1.jpg
in_expiry_hours IN NUMBER DEFAULT 24
) RETURN VARCHAR2
IS
l_response CLOB;
l_access_uri VARCHAR2(4000);
l_expiry_ts VARCHAR2(50);
BEGIN
l_expiry_ts :=
TO_CHAR(
SYSTIMESTAMP AT TIME ZONE 'UTC'
+ NUMTODSINTERVAL(in_expiry_hours, 'HOUR'),
'YYYY-MM-DD"T"HH24:MI:SS"Z"'
);
l_response :=
apex_web_service.make_rest_request(
p_url => 'https://objectstorage.' || in_region ||
'.oraclecloud.com/n/' || in_namespace ||
'/b/' || in_bucket_name || '/p/',
p_http_method => 'POST',
p_body => '{
"name":"img_par_' || DBMS_RANDOM.STRING('X', 8) || '",
"accessType":"ObjectRead",
"objectName":"' || in_object_name || '",
"timeExpires":"' || l_expiry_ts || '"
}'
);
l_access_uri := json_value(l_response, '$.accessUri');
RETURN 'https://objectstorage.' || in_region ||
'.oraclecloud.com' || l_access_uri;
END;
/
## 在 APEX 中使用 PAR
### 在 SQL 中生成 URL
SELECT get_object_par_url(
'ap-mumbai-1',
'a78hhjasl9djjkmz',
'product-images',
'products/p1001.jpg',
6
) AS image_url
FROM dual;
### 在 APEX 中显示
<img src="#IMAGE_URL#" alt="Product Image" loading="lazy">
`#IMAGE_URL#` 是 URL 占位符。
这种方法:
- 快速
- 适合 CDN
- 非常适合卡片、画廊、幻灯片
## 安全方法:通过 APEX 代理 OCI 图片
直接 URL 出于安全考虑受限。

适合以下场景:
- 图片敏感
- 访问取决于用户角色
- 不希望使用公开 URL
### 按需应用进程(APEX)
创建名为 `GET_OBJECT_FILE` 的应用进程。
DECLARE
l_blob BLOB;
l_mime_type VARCHAR2(100);
BEGIN
l_blob := apex_web_service.make_rest_request_b(
p_url => :P0_OBJECT_URL,
p_http_method => 'GET'
);
l_mime_type := apex_util.get_mime_type(:P0_FILE_NAME);
owa_util.mime_header(l_mime_type, FALSE);
htp.p('Content-Disposition: inline; filename="' || :P0_FILE_NAME || '"');
owa_util.http_header_close;
wpg_docload.download_file(l_blob);
apex_application.stop_apex_engine;
END;
### 生成友好 URL
APEX_PAGE.GET_URL(
p_page => 0,
p_request => 'APPLICATION_PROCESS=GET_OBJECT_FILE',
p_items => 'P0_OBJECT_URL,P0_FILE_NAME',
p_values => object_url || ',' || file_name
)
### 在 HTML 中使用
<img src="#IMAGE_URL#" alt="Secure Image">
## 完整的 APEX + OCI 演示流程
以下是大多数实际应用使用的端到端流程:
1. 图片上传到 OCI 存储桶
2. 图片路径存储在表中(仅元数据)
3. APEX 页面查询图片路径
4. 对每张图片:
- 为外部用户生成 PAR
- 或为内部用户通过 APEX 代理
5. 浏览器显示图片
## 图片缓存策略(非常重要)
如果没有缓存,图片页面会显得很慢。
### 对于 PAR URL
- OCI 已支持 HTTP 缓存
- 保持 PAR 过期时间 ≥ 图片缓存生命周期
- 使用浏览器缓存
<img src="PAR_URL" loading="lazy">
最适合:
- 产品图片
- 缩略图
- 公共画廊
### 对于 APEX 代理的图片
添加缓存头:
htp.p('Cache-Control: private, max-age=3600');
htp.p('Pragma: cache');
这允许:
- 浏览器缓存
- 减少 APEX 负载
- 更快的页面渲染
完美适用于:
- PAR URL
- APEX 代理 URL
- 普通 APEX 页面
### 可能出现的错误
`BucketNotFound`:
- 命名空间 `<namespace>` 中不存在名为 `<bucket_name>` 的存储桶或者您无权访问它
## 最终建议
使用以下规则:
- 内部或敏感图片 → APEX 代理
- 公开或高流量图片 → PAR
OCI 对象存储 + APEX 在正确使用时效果极佳。
