APEX:从临时表下载 BLOB

Mic*_*l T 5 oracle plsql oracle-apex

我正在尝试使用 Oracle APEX 4.1.1 构建一个简单的查看应用程序。要显示的信息位于不同数据库的表中,然后是包含 APEX 应用程序访问的架构的数据库。使用视图 ( RemoteTableView ) 和数据库链接访问此远程表。视图按预期工作,包括 Oracle 无法通过数据库链接选择 LOB 列项目。

在 APEX 应用程序中,我根据Oracle Application Express Advanced Tutorials 中的说明定义了一个程序 ( DownloadFiles ),每当需要从视图下载 BLOB 时都会运行该程序

当 APEX 应用程序构建在包含 BLOB 项目的现有表上时,这非常有效,没有问题。

但是,在RemoteTableView 上,过程略有不同。额外的代码行被添加到DownloadFiles过程中,每当调用视图中的项目进行下载时,将RemoteTableView 中的实际 BLOB 插入到临时表 ( TempTable ) 中。然后在TempTable上调用DownloadFile以下载(​​现在本地存储的)BLOB。(这样做是为了避免通过 DB-Link 直接选择 LOB 项)。没有提交。

不幸的是,APEX 应用程序在调用项目下载时失败,并显示“找不到此网页。未找到该网址的网页:.../f?p=101:7:1342995827199601::NO::P7_DOC_ID :3001"。

对这个问题的研究已被证明是徒劳的。插入过程按预期工作(在 PL/SQL Developer 中),并且可以轻松下载任何其他本地表中的任何其他 BLOB。

因此问题是,为什么 APEX 应用程序不能处理这种情况。使用临时表或插入语句时是否有我应该注意的限制?此外,下载 LOB 对象的最佳实践是什么。

详细说明插入行和下载 BLOB 的过程。(我尝试过不同的方法)。这个 PL/SQL 块被称为'on load before header',:P2_BLOB_ID 用标识符列值填充到 BLOB 列。

DECLARE
  v_mime      VARCHAR2(48);
  v_length    NUMBER(38);
  v_file_name VARCHAR2(38);
  Lob_loc     BLOB;
BEGIN
  DELETE FROM [TemporaryTable];
  --
  INSERT INTO [TemporaryTable]( [attr1]
                              , [attr2]
                              , [blob]
                              , [mime] )
  SELECT [attr1]
  ,      [attr2]
  ,      [blob]
  ,      [mime]
  FROM   [RemoteTableView]
  WHERE  [attr1] = :P2_BLOB_ID
  AND    ROWNUM  = 1;
  --
  SELECT [mime]
  ,      [blob]
  ,      [attr1]
  ,      DBMS_LOB.GETLENGTH( [blob] )
  INTO   v_mime
  ,      lob_loc
  ,      v_file_name
  ,      v_length
  FROM   [TemporaryTable]
  WHERE  [attr1] = :P2_BLOB_ID;
  --
  owa_util.mime_header( nvl(v_mime,'application/octet'), FALSE );
  htp.p('Content-length: ' || v_length);
  htp.p('Content-Disposition:  attachment; filename="'||replace(replace(substr(v_file_name,instr(v_file_name,'/')+1),chr(10),null),chr(13),null)|| '"');
  owa_util.http_header_close;
  wpg_docload.download_file( Lob_loc );
END;
Run Code Online (Sandbox Code Playgroud)

Tom*_*Tom 4

尝试在调用后添加apex_application.stop_apex_enginewpg_docload。这将避免进一步输出 HTTP 标头,从而可能会因为生成更多 apex 代码而导致下载失败。

  owa_util.mime_header( nvl(v_mime,'application/octet'), FALSE );
  htp.p('Content-length: ' || v_length);
  htp.p('Content-Disposition:  attachment; filename="'||replace(replace(substr(v_file_name,instr(v_file_name,'/')+1),chr(10),null),chr(13),null)|| '"');
  owa_util.http_header_close;
  wpg_docload.download_file( Lob_loc );
  apex_application.stop_apex_engine;
Run Code Online (Sandbox Code Playgroud)

此外,详细说明:

使用临时表或插入语句时是否有我应该注意的限制?

是的。但在你的情况下不一定。重要的是要记住 apex 在数据库会话方面的工作方式。Apex 是无状态的,并且与连接池一起使用。apex 会话通常不会与 1 个数据库会话匹配,并且您永远无法保证在渲染和处理之间使用相同的数据库会话。关于理解会话状态管理的文档中也简要提到了这一点,为了方便起见复制了:

HTTP 是最常传送 HTML 页面的协议,是一种无状态协议。Web 浏览器仅在下载完整页面时才连接到服务器。此外,每个页面请求都被服务器视为一个独立的事件,与之前发生的或将来可能发生的任何页面请求无关。要在后续页面上访问在一页上输入的表单值,这些值必须存储为会话状态。Oracle Application Express 透明地维护会话状态,并使开发人员能够从应用程序中的任何页面获取和设置会话状态值。

2.4.1 什么是会话?

会话是一种逻辑构造,它在页面视图之间建立持久性(或有状态行为)。每个会话都被分配一个唯一的标识符。Application Express 引擎使用此标识符(或会话 ID)在每次页面视图之前和之后存储和检索应用程序的工作数据集(或会话状态)。

由于会话之间完全独立,因此数据库中可以同时存在任意数量的会话。用户还可以在不同的浏览器中同时运行应用程序的多个实例。

会话在逻辑上和物理上都不同于用于服务页面请求的 Oracle 数据库会话。用户在单个 Oracle Application Express 会话中运行应用程序,从登录到注销的典型持续时间以分钟或小时为单位。该会话期间请求的每个页面都会导致 Application Express 引擎创建或重用 Oracle 数据库会话来访问数据库资源。这些数据库会话通常只持续几分之一秒。

对于全局临时表,这意味着在许多情况下使用它是没有意义的,因为数据仅存在于当前数据库会话中。一个例子是,人们会在 onload 中的某个位置加载 GTT 中的数据,并意味着在提交后流程或 ajax 调用中使用它。桌子很可能是空的。然而,Apex 提供了apex_collection
形式的替代方案,它将临时保存给定 apex 会话中的数据。