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 |
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 |
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
fileIndex = 0; uploadFile(fileIndex); |
Upload File AJAX Callback Process:
Create: AJAX Callback
Process
Name: UPLOAD_FILE
PL/SQL Code:
DECLARE |
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?');
|
Delete File with AJAX Callback Process:
Create Process in AJAX Callback
Name: DELETE_FILE
Paste the below PL/SQL code
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:
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; |