原文地址: https://blogs.ontoorsolutions.com/post/displaying-images-in-oracle-apex-using-oracle-cloud-infrastructure-oci-object-storage/ # 在 Oracle APEX 中使用 OCI 对象存储显示图片 在现代 Oracle APEX 应用程序中,将图片存储在数据库表中通常不是最佳选择。OCI 对象存储提供了更好的可扩展性、性能和成本效益。 ![](https://blogs.ontoorsolutions.com/wp-content/uploads/2026/01/image-11-1024x648.png) ## 为什么选择 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 受限制。 ![](https://blogs.ontoorsolutions.com/wp-content/uploads/2026/01/image-12-1024x310.png) 在以下情况下使用此方法: - 图片敏感 - 访问依赖于用户角色 - 不希望使用公共 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 在正确使用时效果极佳。 ![](https://blogs.ontoorsolutions.com/wp-content/uploads/2024/06/Ontoor-linkedin-Banner-1024x256.png)