在 Oracle APEX 中使用 OCI 对象存储显示图片的完整指南
OCI 对象存储Oracle APEX图片管理安全访问性能优化预认证请求
原文地址: 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 代理(安全)
```
Browser → APEX → OCI Object Storage
```
- 使用凭证由 APEX 获取图片
- 流式传输到浏览器
- 完全会话控制
### 2. 预认证请求(PAR)(快速)
```
Browser → OCI Object Storage
```
- 临时公共 URL
- 不增加 APEX 负载
- 最适合图库和缩略图
我们将从 PAR 开始介绍这两种方法。
## 完整的 OCI PAR 生成代码(PL/SQL)
这个函数生成一个可用于浏览器的 PAR URL,用于单个对象。
### PL/SQL 函数
```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
#### 生成 URL 的 SQL
```sql
SELECT get_object_par_url(
'ap-mumbai-1',
'a78hhjasl9djjkmz',
'product-images',
'products/p1001.jpg',
6
) AS image_url
FROM dual;
```
#### 在 APEX 中显示
```html
<img src="#IMAGE_URL#" alt="Product Image" loading="lazy">
#IMAGE_URL# 是 URL 占位符
```
这种方法:
- 快速
- CDN 友好
- 最适合卡片、图库、幻灯片
## 安全方法:通过 APEX 代理 OCI 图片
出于安全考虑,直接 URL 受限制。

在以下情况下使用此方法:
- 图片敏感
- 访问依赖于用户角色
- 不希望使用公共 URL
### 即时应用程序进程(APEX)
创建一个名为 `GET_OBJECT_FILE` 的应用程序进程。
```sql
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
```sql
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 中使用
```html
<img src="#IMAGE_URL#" alt="Secure Image">
```
## 完整的 APEX + OCI 演示流程
这是大多数真实应用程序使用的端到端流程。
1. 上传图片到 OCI 存储桶
2. 将图片路径存储在表中(仅存储元数据)
3. APEX 页面查询图片路径
4. 对于每张图片:
- 生成 PAR(外部用户)
- 或者通过 APEX 代理(内部用户)
5. 浏览器显示图片
## 图片缓存策略(非常重要)
如果没有缓存,图片页面会显得很慢。
### 对于 PAR URL
- OCI 已经支持 HTTP 缓存
- 保持 PAR 过期时间 ≥ 图片缓存生命周期
- 使用浏览器缓存
```html
<img src="PAR_URL" loading="lazy">
```
最适合:
- 产品图片
- 缩略图
- 公共图库
### 对于 APEX 代理的图片
添加缓存头:
```sql
htp.p('Cache-Control: private, max-age=3600');
htp.p('Pragma: cache');
```
这种方法允许:
- 浏览器缓存
- 减少 APEX 负载
- 更快地渲染页面
与以下内容完美配合:
- PAR URL
- APEX 代理 URL
- 普通 APEX 页面
### 可能的错误
```
BucketNotFound
Either the bucket named <bucket_name> does not exist in the namespace <namespace> or you are not authorized to access it
```
## 最终建议
使用这个规则:
- 内部或敏感图片 → APEX 代理
- 公共或高流量图片 → PAR
OCI 对象存储 + APEX 在正确使用时效果极佳。
