Upload Multiple Files In Oracle Apex (AJAX Callback)

To implement chunked multiple file uploads with AJAX in Oracle APEX, you'll need a storage method for the uploaded files. You can either insert the files directly into a database table or use a temporary collection. In this demo, we utilize the APEX_COLLECTION to store the uploaded files in an AFTER HEADER PL/SQL process.

Steps to Implement Chunked File Upload with AJAX:



Create a Blank Page:

Page Name: Upload Multiple File

Storage for Uploaded Files:

You can insert the uploaded files into a table or store them temporarily in a collection. For this demo, we'll use the UPLOADED_FILES collection in an AFTER HEADER PL/SQL process.

New Region:
- Name: Uploaded Files
- Type: Interactive Report
- SQL Query:

SELECT seq_id, 'UPLOADED_FILES' AS collection_name, c001, c002, DBMS_LOB.getlength(blob001) AS file_size, DBMS_LOB.getlength(blob001) AS blob001, seq_id AS delete_file

FROM apex_collections
WHERE collection_name = 'UPLOADED_FILES'

 




Template: Standard
Static ID: report


 

File Browse Page Item:

Name: P2_FILE_UPLOAD

Type: Upload file

Allow multiple files: Yes


Custom Attributes: multiple (to allow selection of multiple files)


 

Create a New Page Item:

Name: P2_DELETE_FILE_ID

Create an Upload Button:

Button Name: UPLOAD_FILE

 

 

Create a PL/SQL Process:

Trigger: After Header
Name: UPLOADED_FILES

PL/SQL Code:

 

DECLARE
lco_collection_name CONSTANT apex_collections.collection_name%TYPE := 'UPLOADED_FILES';
BEGIN
IF NOT apex_collection.collection_exists(lco_collection_name) THEN
apex_collection.create_collection(p_collection_name => lco_collection_name);
END IF;
END;

 



Create Dynamic Action for the Upload Button:

Name: Collection
Event: Click
Selection Type: Button (UPLOAD_FILE)
Client-side Condition:
Type: JavaScript Expression
Expression: fileInputElem.files.length != 0
Action: Execute JavaScript Code

 spinner = new Spinner(spinOptions).spin(spinTargetElem);

fileIndex = 0;

uploadFile(fileIndex); 

 

 

Upload File AJAX Callback Process:

Create: AJAX Callback Process

Name: UPLOAD_FILE
PL/SQL Code:

 

 

DECLARE
lco_collection_name CONSTANT apex_collections.collection_name%TYPE := 'UPLOADED_FILES';
l_blob BLOB;
l_filename VARCHAR2(200);
l_mime_type VARCHAR2(200);
l_token VARCHAR2(32000);
BEGIN
l_filename := APEX_APPLICATION.g_x01;
l_mime_type := NVL(APEX_APPLICATION.g_x02, 'application/octet-stream');
DBMS_LOB.createtemporary(l_blob, FALSE, DBMS_LOB.session);
FOR i IN 1 .. APEX_APPLICATION.g_f01.COUNT LOOP
l_token := wwv_flow.g_f01(i);
IF LENGTH(l_token) > 0 THEN
DBMS_LOB.append(dest_lob => l_blob, src_lob => to_blob(UTL_ENCODE.base64_decode(UTL_RAW.cast_to_raw(l_token))));
END IF;
END LOOP;
IF DBMS_LOB.getlength(l_blob) IS NOT NULL THEN
apex_collection.add_member(p_collection_name => lco_collection_name, p_c001 => l_filename, p_c002 => l_mime_type, p_blob001 => l_blob);
END IF;
apex_json.open_object;
apex_json.write(p_name => 'result', p_value => 'success');
apex_json.close_object;
EXCEPTION
WHEN OTHERS THEN
apex_json.open_object;
apex_json.write(p_name => 'result', p_value => 'fail');
apex_json.close_object;
END;

 



Download and Upload JS File:

Download the JavaScript file for handling the spinner and upload logic.





After downloading, upload it to Static Application Files under Shared Components.

Page Level JavaScript File URLs Reference: #APP_FILES#spin.min.js

Copy the provided JavaScript and paste it between Function and Global Variable Declaration on the page.

var fileInputElem = document.getElementById('P2_FILE_UPLOAD');

var fileIndex = 0;

function clob2Array(clob, size, array) {

  var loopCount = Math.floor(clob.length / size) + 1;

  for (var i = 0; i < loopCount; i++) {

    array.push(clob.slice(size * i, size * (i + 1)));

  }

  return array;

}

function binaryArray2base64(int8Array) {

  var data = "";

  var bytes = new Uint8Array(int8Array);

  var length = bytes.byteLength;

  for (var i = 0; i < length; i++) {

    data += String.fromCharCode(bytes[i]);

  }

  return btoa(data);

}

function uploadFile(pFileIndex) {

  var file = fileInputElem.files[pFileIndex];

  var reader = new FileReader();

  reader.onload = (function(pFile) {

    return function(e) {

      if (pFile) {

        var base64 = binaryArray2base64(e.target.result);

        var f01Array = [];

        f01Array = clob2Array(base64, 30000, f01Array);

        apex.server.process(

          'UPLOAD_FILE',

          {

            x01: file.name,

            x02: file.type,

            f01: f01Array

          },

          {

            dataType: 'json',

            success: function(data) {

              if (data.result === 'success') {

                fileIndex++;

                if (fileIndex < fileInputElem.files.length) {

                  uploadFile(fileIndex);

                } else {

                  spinner.stop();

                  fileInputElem.value = '';

                  $('#report').trigger('apexrefresh');

                }

              } else {

                alert('Oops! Something went terribly wrong. Please try again or contact your application administrator.');

              }

            },

            error: function(jqXHR, textStatus, errorThrown) {

              spinner.stop();

              alert('Error uploading file: ' + textStatus + ' - ' + errorThrown);

            }

          }

        );

      }

    }

  })(file);

  reader.readAsArrayBuffer(file);

}

var spinner;

var spinTargetElem = document.getElementById('wwvFlowForm');

var spinOptions = {

  lines: 13,

  length: 28,

  width: 14,

  radius: 42,

  scale: 1,

  corners: 1,

  color: '#000',

  opacity: 0.25,

  rotate: 0,

  direction: 1,

  speed: 1,

  trail: 60,

  fps: 20,

  zIndex: 2e9,

  className: 'spinner',

  top: '50%',

  left: '50%',

  shadow: false,

  hwaccel: false,

  position: 'absolute'

};

 

File Deletion:

Go to Column: DELETE_FILE

Type: link

Link Target: javascript:$s('P230_DELETE_FILE_ID', #SEQ_ID#);void(0);




Link Text: <span class="t-Icon fa fa-trash delete-irrow" aria-hidden="true">#DELETE_FILE#</span>


 

Confirmation and Delete Process:
Create Dynamic action under the P2_DELETE_FILE_ID




Paste below code in Execute JavaScript Code

 

var confirmDialog = confirm('Are you sure you want to delete this file?');
if (confirmDialog == true) {
spinner = new Spinner(spinOptions).spin(spinTargetElem);
apex.server.process(
'DELETE_FILE',
{ x01: $v('P2_DELETE_FILE_ID') },
{
dataType: 'json',
success: function(data) {
spinner.stop();
if (data.result == 'success') {
$('#report').trigger('apexrefresh');
} else {
alert('Failed to delete the file. Please try again.');
}
}
}
);
}

 

 

Delete File with AJAX Callback Process:

Create Process in AJAX Callback

Name: DELETE_FILE


 

Paste the below PL/SQL code

 BEGIN

   apex_collection.delete_member (

       p_collection_name   => 'UPLOADED_FILES',

       p_seq               => APEX_APPLICATION.g_x01);

   apex_json.open_object;

   apex_json.write (p_name => 'result', p_value => 'success');

   apex_json.close_object;

EXCEPTION

   WHEN OTHERS

   THEN

       apex_json.open_object;

       apex_json.write (p_name => 'result', p_value => 'fail');

       apex_json.close_object;

END;

 

Save Data to Custom Table:

Create Save Button: Name - SAVE_DATA

Create Process : Name - SAVE_DATA

Process Code:

 DECLARE

v_id number;

   -- get files data from saved apex_collection

   CURSOR l_cur_files IS

       SELECT c001        AS filename,

              c002        AS mime_type,

              d001        AS date_created,

              n001        AS file_id,

              blob001     AS file_content

         FROM apex_collections

        WHERE collection_name = 'UPLOADED_FILES';

BEGIN

SELECT nvl(max(id),0)+1

into v_id

  FROM upload_multi_files;

   -- loop over files cursor

   FOR l_rec_files IN l_cur_files

   LOOP

       -- do whatever processing is required prior to the insert into your own table

       INSERT INTO upload_multi_files(id,

                                       mime_type,

                                       tds_file,

                                       upload_by,

                                       upload_date

                                 )

            VALUES (v_id,

                    l_rec_files.mime_type,

                    l_rec_files.file_content,

                    :APP_USER,

                    sysdate

                    );

                    v_id := v_id+1;

   END LOOP;

   -- clear original apex collection (only if exist)

   IF apex_collection.collection_exists (

          p_collection_name   => 'UPLOADED_FILES')

   THEN

       apex_collection.delete_collection (

           p_collection_name   => 'UPLOADED_FILES');

   END IF;

END;










Post a Comment

Previous Post Next Post