原文地址: 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 不允许公开访问,这很好。它强制要求明确指定图片的暴露方式。 ![](https://blogs.ontoorsolutions.com/wp-content/uploads/2026/01/image-11-1024x648.png) ## 在 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 出于安全考虑受限。 ![](https://blogs.ontoorsolutions.com/wp-content/uploads/2026/01/image-12-1024x310.png) 适合以下场景: - 图片敏感 - 访问取决于用户角色 - 不希望使用公开 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 在正确使用时效果极佳。 ![](https://blogs.ontoorsolutions.com/wp-content/uploads/2024/06/Ontoor-linkedin-Banner-1024x256.png)