WEBDAV Fileupload

WEBDAV Fileupload

Some of our Customers use Webdav (nextcloud) Fileserver to store theire Files.
So we need a integration in Oracle Apex:

  1. First we need a Upload File Item

  1. Then we need to Load it into the nextcloud Webdav API Service: Calling our Function

DECLARE
    l_files          apex_t_varchar2;
    v_result         number;
BEGIN
--- P210_CONTENTS is the Item for Upload
    l_files := apex_string.split(:P210_CONTENTS, ':');
    FOR i IN 1..l_files.COUNT LOOP
        p_log.log_debug('file upload start for ' || l_files(i));
        v_result := p_fil_webdav.upload_file('', l_files(i));
        p_log.log_debug('result upload_file: ' || v_result );
    END LOOP;

EXCEPTION
    WHEN OTHERS THEN
        -- Exception handling
        p_log.log_error('An error occurred: ' || SQLERRM);
        RAISE;
END;

The main doing is in our Procedure p_fil_webdav.upload_file

We tried it first with APEX_WEB_SERVICE but we got a Cookie not set error so we decided to use UTL_HTTP to handle this correctly.

  1. The following function uploads a file from Oracle APEX's apex_application_temp_files table to a Nextcloud server using the UTL_HTTP package.

Function Overview

FYI: Basic APIs — Nextcloud latest Developer Manual latest documentation

The upload_file function:

  1. Retrieves file metadata and content from APEX's temporary file storage.

  2. Ensures the target directory exists on Nextcloud.

  3. Authenticates and sends the file to Nextcloud using HTTP PUT requests.

  4. Handles errors gracefully and logs important steps for debugging.

Code Walkthrough

Below, we break the function into meaningful sections and describe their purpose.


1. Function Declaration and Initialization

FUNCTION upload_file(p_dir VARCHAR2, p_filename VARCHAR2) RETURN VARCHAR2 
AS 
    -- HTTP request/response objects
    v_http_request   UTL_HTTP.req;
    v_http_response  UTL_HTTP.resp;
    v_url            VARCHAR2(1000);

    -- Authentication and wallet details
    v_username       VARCHAR2(1000);
    v_password       VARCHAR2(1000);
    v_wallet_path    VARCHAR2(200);
    v_wallet_password VARCHAR2(200);

    -- File handling variables
    v_blob           BLOB;
    v_content_length NUMBER := 0;
    v_offset         INTEGER;
    v_buffer         RAW(32767);
    v_chunk_size     PLS_INTEGER := 32767;

    -- Miscellaneous
    v_filename       VARCHAR2(500);
    v_start          DATE;
    v_end            DATE;
    nextcloud_dir_create_error EXCEPTION;
    upload_multiple_loops_error EXCEPTION;
BEGIN  
    p_log.log_debug('upload_file ' || p_dir || ':' || p_filename);
  • Parameters:

    • p_dir: Target directory on Nextcloud.

    • p_filename: File to be uploaded.

  • Purpose: Initializes variables and logs the function call.


2. Retrieve File from APEX Temporary Table

FOR c1 IN (
    SELECT application_id, name, filename, mime_type, blob_content
    FROM apex_application_temp_files
    WHERE name = p_filename
) LOOP
    IF (v_cycle > 0) THEN
        RAISE upload_multiple_loops_error;
    END IF;
    v_cycle := 1;
    v_blob := c1.blob_content;
    v_content_length := DBMS_LOB.getlength(v_blob);
  • Purpose: Queries the file content and metadata from the temporary files table.

  • Error Handling: Ensures only one file matches p_filename by using a cycle counter.


3. Directory Validation

v_result := create_directory(p_dir);
IF v_result = 0 THEN
    RAISE nextcloud_dir_create_error;
END IF;
v_filename := determine_filename(p_dir, c1.filename);
  • Directory Check: Calls create_directory to verify or create the target directory.

  • Error Handling: Raises an exception if directory creation fails.


4. Authentication Setup

--- In our Case important!
UTL_HTTP.SET_COOKIE_SUPPORT(TRUE);

v_wallet_path := p_adm.get_param('wallet_path','');
v_wallet_password := p_adm.get_param_base64('wallet_password','');

UTL_HTTP.SET_WALLET(v_wallet_path, v_wallet_password);

v_username := p_adm.get_param('nextcloud_username','');
v_password := p_adm.get_param_base64('nextcloud_password','');

v_url := p_adm.get_param('nextcloud_basic_url','') || 
         UPPER(v_username) || '/' || 
         CASE WHEN p_dir IS NULL THEN '' ELSE p_dir || '/' END || 
         UTL_URL.escape(v_filename);
  • Wallet Configuration: Configures the Oracle Wallet for secure HTTPS communication.

  • Authentication: Uses basic authentication for Nextcloud.

  • Dynamic URL: Constructs the target URL using parameters.


5. Send File Using HTTP PUT Request

v_http_request := UTL_HTTP.begin_request(v_url, 'PUT', 'HTTP/1.1');
UTL_HTTP.set_authentication(v_http_request, v_username, v_password, 'Basic');
-- Nextcloud specific
UTL_HTTP.set_header(v_http_request, 'OCS-APIREQUEST', 'true');

UTL_HTTP.set_header(v_http_request, 'Content-Type', c1.mime_type);
UTL_HTTP.set_header(v_http_request, 'Content-Length', v_content_length);

v_offset := 1;
WHILE v_offset <= v_content_length LOOP
    v_amount := LEAST(32767, v_content_length - v_offset + 1);
    DBMS_LOB.read(v_blob, v_amount, v_offset, v_buffer);
    UTL_HTTP.write_raw(v_http_request, v_buffer);
    v_offset := v_offset + v_amount;
END LOOP;
v_http_response := UTL_HTTP.get_response(v_http_request);
  • Request Initialization: Creates an HTTP PUT request and sets necessary headers.

  • File Streaming: Reads the BLOB in chunks and writes it to the HTTP request body.

  • Response Handling: Captures the server's response after file upload.


6. Response Handling and Logging

BEGIN
    LOOP
        UTL_HTTP.READ_TEXT(r => v_http_response, data => v_data);
        p_log.log_debug('Response data: ' || v_data);
    END LOOP;
EXCEPTION
    WHEN UTL_HTTP.END_OF_BODY THEN
        NULL;  -- End of response body reached
END;
UTL_HTTP.end_response(v_http_response);

IF v_http_response.status_code = 201 THEN
    RETURN v_filename;
ELSE
    RETURN 'NOK';
END IF;
  • Purpose: Processes the server's response.

  • Success Check: Confirms the status code (201 Created) for successful upload.

  • Return Value: Returns the uploaded filename or 'NOK' for failure.


7. Exception Handling

EXCEPTION
    WHEN OTHERS THEN
        p_log.log_error('An error occurred: ' || SQLERRM);
        RAISE;
END upload_file;
  • Error Logging: Logs unexpected errors for debugging.

  • Re-Raises Errors: Ensures unhandled exceptions propagate.


Key Features of the Function

  1. Secure Communication: Leverages Oracle Wallet for HTTPS.

  2. Chunked Uploads: Handles large files efficiently by reading in chunks.

  3. Robust Logging: Uses a custom p_log package for debugging and monitoring.

  4. Dynamic Configuration: Retrieves parameters (e.g., usernames, passwords) at runtime.


Example Usage

DECLARE
    v_result VARCHAR2(500);
BEGIN
    v_result := upload_file('Documents', 'example.pdf');
    DBMS_OUTPUT.PUT_LINE('Upload result: ' || v_result);
END;

Happy Uploading into nextcloud/webdav