I have an Interactive Report with column having arabic Characters displaying well in the Report , however i am exporting the Reports into csv but the arabic characters turns to ????? in csv file , Any suggestions or workaround i may follow either at Interactive Report level or query level or this something csv don't support.
I am not entirely sure, but I think the problem might happen because the Java script function associated with Actions Menu --> Download might not take in consideration the encoding on database side, rather than the one on client side.
Normally, when I want to control the export to csv from a page, I disable the actions menu to avoid that the user can do it using that menu, instead I prefer to create a PL/SQL procedure to be triggered by an application express process.
How to do that ?
Download CSV File Using PL/SQL Procedure and Application Process in Oracle Apex
In order to do this , follow the instructions:
1.Create a PL/SQL Procedure
Create a database procedure which will return the CSV as CLOB data.
create or replace procedure tab_to_csv(o_Clobdata OUT CLOB) IS
l_Blob BLOB;
l_Clob CLOB;
BEGIN
Dbms_Lob.Createtemporary(Lob_Loc => l_Clob,
Cache => TRUE,
Dur => Dbms_Lob.Call);
SELECT Clob_Val
INTO l_Clob
FROM (SELECT Xmlcast(Xmlagg(Xmlelement(e,
Col_Value || Chr(13) ||
Chr(10))) AS CLOB) AS Clob_Val,
COUNT(*) AS Number_Of_Rows
FROM (SELECT 'your columns for the header split by the separator' AS Col_Value
FROM Dual
UNION ALL
SELECT col1||',' ||col2||','|| col3||','|| col4||','|| col5||','|| col6 as Col_Value
FROM (SELECT col1,col2,col3,col4,col5,col6 from yourtable)));
o_Clobdata := l_Clob;
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
/
You can adapt that procedure the way you want. I use header, so that is the reason for the first select. In my example, the separator was , , but you can use another one if you like, or even use a parameter for it instead.
2.Create an Application Process in Oracle Apex
In Oracle Apex, click on the Shared Components --> Application Process and then click on the Create button. Then follow these steps:
Then press next and put the following code
DECLARE
L_BLOB BLOB;
L_CLOB CLOB;
L_DEST_OFFSET INTEGER := 1;
L_SRC_OFFSET INTEGER := 1;
L_LANG_CONTEXT INTEGER := DBMS_LOB.DEFAULT_LANG_CTX;
L_WARNING INTEGER;
L_LENGTH INTEGER;
BEGIN
-- create new temporary BLOB
DBMS_LOB.CREATETEMPORARY(L_BLOB, FALSE);
--get CLOB
tab_to_csv( L_CLOB);
-- tranform the input CLOB into a BLOB of the desired charset
DBMS_LOB.CONVERTTOBLOB( DEST_LOB => L_BLOB,
SRC_CLOB => L_CLOB,
AMOUNT => DBMS_LOB.LOBMAXSIZE,
DEST_OFFSET => L_DEST_OFFSET,
SRC_OFFSET => L_SRC_OFFSET,
BLOB_CSID => NLS_CHARSET_ID('WE8MSWIN1252'),
LANG_CONTEXT => L_LANG_CONTEXT,
WARNING => L_WARNING
);
-- determine length for header
L_LENGTH := DBMS_LOB.GETLENGTH(L_BLOB);
-- first clear the header
HTP.FLUSH;
HTP.INIT;
-- create response header
OWA_UTIL.MIME_HEADER( 'text/csv', FALSE, 'AL32UTF8');
HTP.P('Content-length: ' || L_LENGTH);
HTP.P('Content-Disposition: attachment; filename="yourfile.csv"');
HTP.P('Set-Cookie: fileDownload=true; path=/');
OWA_UTIL.HTTP_HEADER_CLOSE;
-- download the BLOB
WPG_DOCLOAD.DOWNLOAD_FILE( L_BLOB );
-- stop APEX
-- APEX_APPLICATION.STOP_APEX_ENGINE;
EXCEPTION
WHEN OTHERS THEN
DBMS_LOB.FREETEMPORARY(L_BLOB);
RAISE;
END;
After that click on the Next button and on the next screen click on the Create button to finish the wizard. Your application process has been created.
3.Create a Button on a Page in Oracle Apex
Now open a page in Page designer in Oracle Apex in which you want to add a button to download the CSV file.
Then do the right-click on the Region and click on the option Create Button.
Set the Action to Redirect to URL.
Paste the following URL in the URL target.
f?p=&APP_ID.:0:&SESSION.:APPLICATION_PROCESS=download_emp_csv:NO
Notice that we are calling the application process download_emp_csv, we just created in the second step.
Now save the changes and run the page. On click of the button, the CSV file will be download.
Related
I've created an apex application setting FOO with value bar following these instructions.
When I try to access the setting from the SQL Workshop > SQL Commands page I see
Requested Application Setting #FOO# is not defined. Here is the SQL command I am running to retrieve the value:
declare
l_value varchar2(4000);
begin
l_value := APEX_APP_SETTING.GET_VALUE( p_name => 'FOO');
dbms_output.put_line(l_value);
end;
Any idea why this won't work?
Since you are not executing the code from within an APEX session, you need create an APEX session or attach yourself to to an existing APEX session otherwise there is no way to know what workspace/application to get the setting from. If the code was running from within a page, then your existing code would work.
Try executing the code below, but modify your APP ID and PAGE ID to an actual app/page within your APEX application. The username does not matter.
DECLARE
l_value VARCHAR2 (4000);
BEGIN
apex_session.create_session (p_app_id => 100, p_page_id => 2, p_username => 'any_username');
l_value := APEX_APP_SETTING.GET_VALUE (p_name => 'FOO');
DBMS_OUTPUT.put_line (l_value);
apex_session.delete_session;
END;
If you want to try building a function to return the setting value, I would recommend building a function like this
CREATE OR REPLACE FUNCTION FN_APP_CONFIG (
p_setting_name APEX_190200.WWV_FLOW_APP_SETTINGS.NAME%TYPE,
p_app_id APEX_190200.WWV_FLOW_APP_SETTINGS.FLOW_ID%TYPE DEFAULT APEX_APPLICATION.g_flow_id)
RETURN APEX_190200.WWV_FLOW_APP_SETTINGS.VALUE%TYPE
IS
l_value APEX_190200.WWV_FLOW_APP_SETTINGS.VALUE%TYPE;
BEGIN
SELECT VALUE
INTO l_value
FROM APEX_190200.WWV_FLOW_APP_SETTINGS
WHERE flow_id = p_app_id AND name = p_setting_name;
RETURN l_value;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
RETURN NULL;
WHEN TOO_MANY_ROWS
THEN
RETURN NULL;
END;
/
I am trying to find a way to get files from the database server via APEX. I can't find any documentation about this issue.
Can I avoid using the plsql code?
PS: I'm launching APEX on tomcat on different server that db is.
As we talked on the comments, let me show you an example. I write you a lot of thing so take your time...
Upload file
You can have an object in Apex that allows the user to browse for a file to be uploaded. At the end, the user press a button that triggers the action . The button upload submits the page, and the action is after submit for the button itself. Any file upload is stored automatically in apex_application_temp_files ( keep in mind I removed a lot of controls I have regarding format of the file, size, etc ).
First create the directory
create or replace directory yourdirectory as '/your_path' ;
grant read, write on directory yourdirectory to your_user ;
The code in the button:
declare
v_error VARCHAR2(400);
v_filename VARCHAR2(400);
v_name VARCHAR2(400);
v_blob blob;
vodate number(8);
begin
SELECT filename,blob_content,name, to_number(regexp_replace(filename,'[0-9]{4}[0-9]{2}[0-9]{2}'))
INTO v_filename,v_blob,v_name,vodate
FROM apex_application_temp_files
WHERE name = :P2_FILE;
apex_debug.enable ( p_level => 5 );
apex_debug.message(p_message => 'v_filename is '||v_filename||' ', p_level => 5) ;
apex_debug.message(p_message => 'v_name is '||v_name||' ', p_level => 5) ;
apex_debug.message(p_message => 'vodate is '||to_number(substr(v_filename,14,8)) ||' ', p_level => 5) ;
-- insert into filesystem
p_write_blob_to_file(p_name=>v_name);
EXCEPTION
WHEN OTHERS THEN
raise;
end;
The important part here is the code p_write_blob_to_file. This is the code of that procedure, keeping in consideration that in my case p_dir takes a default value.
CREATE OR REPLACE procedure p_write_blob_to_file (p_name IN VARCHAR2, p_dir IN VARCHAR2 default 'your_directory' )
IS
l_blob BLOB;
l_blob_length INTEGER;
l_out_file UTL_FILE.file_type;
l_buffer RAW (32767);
l_chunk_size BINARY_INTEGER := 32767;
l_blob_position INTEGER := 1;
l_file_name varchar2(2000);
v_mime_type varchar2(2000);
BEGIN
-- Retrieve the BLOB for reading
SELECT blob_content, filename, mime_type
INTO l_blob, l_file_name, v_mime_type
FROM apex_application_temp_files
WHERE name = p_name;
-- Retrieve the SIZE of the BLOB
l_blob_length := DBMS_LOB.getlength (l_blob);
-- Open a handle to the location where you are going to write the BLOB
-- to file.
l_out_file :=
UTL_FILE.fopen (p_dir,
l_file_name,
'wb',
l_chunk_size);
-- Write the BLOB to file in chunks
WHILE l_blob_position <= l_blob_length
LOOP
IF l_blob_position + l_chunk_size - 1 > l_blob_length
THEN
l_chunk_size := l_blob_length - l_blob_position + 1;
END IF;
DBMS_LOB.read (l_blob,
l_chunk_size,
l_blob_position,
l_buffer);
UTL_FILE.put_raw (l_out_file, l_buffer, TRUE);
l_blob_position := l_blob_position + l_chunk_size;
END LOOP;
-- Close the file handle
UTL_FILE.fclose (l_out_file);
END p_write_blob_to_file;
/
Download File
In order to download the file, you need the opposite path.
The button or link download must be associated to a component PL/SQL
The button loads the file first from the directory in the server into a column
The button then download the file
I was going to write all the commands here , but you have a find very good example here of both actions:
Load file from directory into column
https://renaps.com/en/blog/how-to/how-to-load-file-content-to-a-blob-field-and-unload-blob-content-to-a-file-on-the-os
Download file
https://oracle-base.com/articles/misc/apex-tips-file-download-from-a-button-or-link#apex-button
Try to experiment with this and let me know any issues you might find. The only tricky thing here is that in Apex you need to pass the name of the file you want to download. So the user must know the name, exactly as it is in the server. What you can't do is provide a graphical interface to the server in order to select the file.
I am getting this error:
Error: SyntaxError: JSON.parse Error: Invalid character at position:1
on executing the below code in oracle apex5.2 during submit page,
below code is for downloading a file using pl/sql in oracle apex.
I created a button; when we click that button the below code will execute and submit page also will happen.
Declare
dest_loc11 BLOB := empty_blob();
dest_loc BLOB := empty_blob();
dest_loc2 BLOB := empty_blob();
dest_loc3 BLOB := empty_blob();
dest_loc4 BLOB := empty_blob();
dest_loc5 BLOB := empty_blob();
dest_loc6 BLOB := empty_blob();
src_loc BLOB := empty_blob();
l_zip_file blob;
v_length integer;
v_inp varchar2(32767);
v_count number;
n number:=1;
v_pr1_idx number;
v_pr1_idx2 number;
V_ID NUMBER;
v_pr_1 varchar2(32767):='130614';
V_RES VARCHAR2(32767);
V_PRINT VARCHAR2(32767):='';
V_PROMPT VARCHAR2(32767);
V_PRINT_RAW BLOB;
csv_file utl_file.file_type;
V_NO_DATA varchar2(32767);
v_pr_11 varchar2(32767);
BEGIN
DBMS_LOB.CREATETEMPORARY(
lob_loc => dest_loc11,
cache => true,
dur => dbms_lob.session
);
DBMS_LOB.OPEN(dest_loc11, DBMS_LOB.LOB_READWRITE);
V_PRINT:='udfhsdhgfszhduhjsdzvcjhzxjcvhzxc';
V_PRINT_RAW := utl_raw.cast_to_raw( V_PRINT );
v_length := dbms_lob.getlength(V_PRINT_RAW);
DBMS_LOB.WRITEAPPEND (
lob_loc => dest_loc11,
amount => v_length,
buffer => V_PRINT_RAW);
DBMS_LOB.CLOSE(dest_loc11);
sys.htp.init;
sys.owa_util.mime_header( 'text/plain', FALSE );
sys.htp.p('Content-length: ' || sys.dbms_lob.getlength( dest_loc11));
sys.htp.p('Content-Disposition: attachment; filename="' ||'bala.sql' || '"' );
sys.htp.p('Cache-Control: max-age=3600');-- tell the browser to cache for one hour, adjust as necessary
sys.owa_util.http_header_close;
sys.wpg_docload.download_file( dest_loc11);
DBMS_LOB.FREETEMPORARY (dest_loc11);
apex_application.stop_apex_engine;
end;
You need to set Reload on Submit to Always at page level in the page designer. Then you can successfully download the file. Also your code has a chance of raising Value error when v_length is less than 1 . Make sure you eliminate appending the text to the blob when v_length is outside the limits 1-32767
It sounds like you have a PL/SQL Dynamic Action that fires when the button is clicked, then that downloads your file? Nope, you can't download a file via Ajax.
Download a file by jQuery.Ajax
What you need to do is redirect the user to a different page, or back to the current page with a different REQUEST value, so you know you're supposed to be downloading the file. Then, put your code in a Before Header process on that page.
This link explains how to show an image from a BLOB in a table, to a Display image item
http://www.apexninjas.com/blog/2011/09/uploading-and-displaying-images-in-apex/
It advises to write this code (the code converts BLOB data into HTML):
create or replace PROCEDURE image(image_id IN NUMBER)
AS
l_mime VARCHAR2 (255);
l_length NUMBER;
l_file_name VARCHAR2 (2000);
lob_loc BLOB;
BEGIN
SELECT i.MIME_TYPE, i.CONTENT, DBMS_LOB.getlength (i.CONTENT), i.FILENAME
INTO l_mime, lob_loc, l_length, l_file_name
FROM EMP_IMAGE i
WHERE i.ID = image_id;
OWA_UTIL.mime_header (NVL (l_mime, 'application/octet'), FALSE);
htp.p('Content-length: ' || l_length);
htp.p('Content-Disposition: filename="' || SUBSTR(l_file_name, INSTR(l_file_name, '/') + 1) || '"');
owa_util.http_header_close;
wpg_docload.download_file(Lob_loc);
END image;
I'm new to Oracle APEX 5, and I do not understand where in the interface should I write that code
My UI loos like this
https://i.imgur.com/7xCRO7U.png
They're telling you to create that procedure in the database; it's not an APEX component. I don't think I would recommend their approach of opening a security hole to allow direct execution of a custom local procedure. And it's not necessary anymore; APEX has changed a lot since 2011.
On the left, click your Display Image item named "IMAGE". Then on the right, under "Settings", change the "Based On" to BLOB Column returned by a SQL Statement, and just put a query in there that'll return the column you want.
You'll need a primary key to find the right row; here I'm assuming that you're using the FILE_NAME page item, but probably a better option would be to have a hidden page item to hold the primary key from your IMAGES table, maybe called "P1_ID" for an "id" column, and add RETURNING id INTO :P1_ID to the end of your INSERT statement in your page process. Then your "IMAGE" page item query will look something like:
SELECT i.CONTENT
FROM IMAGES i
WHERE i.ID = :P1_ID;
I created a Concurrent Program that creates an Excel File from a long, parametrized query using PL/SQL.
Once the Program successfully completes, the file is placed in the remote server's directory and is usually around 4 MB in Size.
I'm thinking of an approach to notify the requestor and enable him/her to save the file to their local directory.
However, I cannot use UTL_MAIL to attach and send the file via email due to the 32 Kilobyte Limitation. (Does UTL_MAIL have an attachment limit of 32k).
In the same post, Tom Kyte preferred approach would be to:
store the attachment to the database.
email a very small email with a link. the link points to my database - using a URL.
With that, i was thinking taking the same approach and use the block below to notify the requestor and enable him/her to download the said Excel file:
declare
l_url_link varchar2(100); -- how can i get the URL of the File?
BEGIN
UTL_MAIL.SEND(sender => 'xxx#oracle.com'
, recipients => 'Migs.Isip.23#Gmail.com'
, subject => 'Testmail'
, message => 'Your File is Ready to be downloaded, click the link here: '||l_url_link);
END;
My Questions would be:
How can i generate the "URL" of the Remote file using PL/SQL?
Do the users need to be granted access to the remote server to download the file?
Thank you!
Oracle Database Version:
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
"CORE 11.2.0.4.0 Production"
TNS for Solaris: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 - Production
Here is a pl/sql function I wrote to retrieve the URL of either the concurrent log file or output file. If you write your Excel file to the concurrent output, this should work fine. Let me know how you get on. I have not checked to see if this will give the correct mime-type or extension - not sure how EBS handles this but the function itself will definitely compile as is for 12.1.3.
Spec
FUNCTION get_concurrent_url (p_file_type IN VARCHAR2
,p_request_id IN NUMBER
,p_expiry IN NUMBER)
RETURN VARCHAR2;
Body
/* Get a URL to view the log/output
File Type is LOG or OUT
Request ID is the concurrent request ID
Expiry is in minutes */
FUNCTION get_concurrent_url (p_file_type IN VARCHAR2
,p_request_id IN NUMBER
,p_expiry IN NUMBER)
RETURN VARCHAR2
IS
CURSOR c_gwyuid
IS
SELECT profile_option_value
FROM fnd_profile_options FPO
,fnd_profile_option_values FPOV
WHERE FPO.profile_option_name = 'GWYUID'
AND FPO.application_id = FPOV.application_id
AND FPO.profile_option_id = FPOV.profile_option_id;
CURSOR c_two_task
IS
SELECT profile_option_value
FROM fnd_profile_options FPO
,fnd_profile_option_values FPOV
WHERE FPO.profile_option_name = 'TWO_TASK'
AND FPO.application_id = FPOV.application_id
AND FPO.profile_option_id = FPOV.profile_option_id;
l_request_id NUMBER;
l_file_type VARCHAR2 (3 BYTE);
l_expiry NUMBER;
l_two_task VARCHAR2 (100 BYTE);
l_gwyuid VARCHAR2 (100 BYTE);
l_url VARCHAR2 (1024 BYTE);
BEGIN
l_request_id := p_request_id;
l_file_type := p_file_type;
l_expiry := p_expiry;
FOR i IN c_gwyuid LOOP
l_gwyuid := i.profile_option_value;
END LOOP;
FOR i IN c_two_task LOOP
l_two_task := i.profile_option_value;
END LOOP;
IF l_file_type = 'LOG' THEN
l_url := fnd_webfile.get_url
(file_type => fnd_webfile.request_log
,id => l_request_id
,gwyuid => l_gwyuid
,two_task => l_two_task
,expire_time => l_expiry);
ELSE
l_url := fnd_webfile.get_url
(file_type => fnd_webfile.request_out
,id => l_request_id
,gwyuid => l_gwyuid
,two_task => l_two_task
,expire_time => l_expiry);
END IF;
RETURN l_url;
END get_concurrent_url;
I was able to find a solution for this using a (slightly different) method using the FND_GFM File Uploader Package in Oracle EBS.
FND_GFM is a package usually used in Oracle EBS when uploading files from the front-end application pages.
First, generate the Excel file (xlsx) using the code from the previous post: Create an Excel File (.xlsx) using PL/SQL,
Then the file is inserted into FND_LOBS and removed the from the OS (for good housekeeping), and finally sent as an email using UTL_FILE:
procedure generate_and_send_excel
is
l_content varchar2(250);
l_file_url varchar2(4000);
l_directory varchar2(250);
l_filename varchar2(250);
l_message clob;
l_instance varchar2(100);
l_ebs_url varchar2(100);
begin
/* your excel generation code here */
l_content := 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
l_directory := 'EXT_TAB_DATA';
l_filename := 'report.xlsx';
select instance_name
into l_instance
from v$instance;
select home_url
into l_ebs_url
from icx_parameters;
IMPORT_TO_LOB (p_file_name => l_filename -- this is the actual filename of the saved OS File
, p_directory => l_directory -- should be a defined directory in the Database
, p_content_type => l_content -- standard for Excel Files
, p_program_name => 'your prog here'
, p_program_tag => 'your prog here'
, p_file_url => l_file_url); -- this will be the generated URL of your File
utl_file.fremove(l_directory, l_filename);
l_message := l_message||'<h2 style="color: #5e9ca0;">'||l_title||'</h2>';
l_message := l_message||'<h3 style="color: #2e6c80;">Report is Ready for Download: '||l_filename||'</h3>';
l_message := l_message||'<p>File was generated on '|| sysdate ||' from '||l_instance||'</p>';
l_message := l_message||'<strong>Regards,</strong><br/><strong>Sample Team</strong>';
l_message := l_message||'<br/>Sample#sample.com';
UTL_MAIL.SEND(sender => 'SAMPLE#SAMPLE.com'
, recipients => 'Migs.Isip.23#gmail.com'
, subject => 'Hello message'
, message => l_message
, mime_type => 'text/html; charset=us-ascii');
end generate_and_send_excel;
Procedure below to insert into FND_LOBS (there's no available seeded API):
Procedure IMPORT_TO_LOB (p_file_name IN FND_LOBS.FILE_NAME%TYPE
, p_directory IN dba_directories.directory_name%type
, p_content_type IN FND_LOBS.file_content_type%type
, p_program_name IN FND_LOBS.program_name%type
, p_program_tag IN FND_LOBS.program_tag%type
, p_language IN FND_LOBS.language%type default 'US'
, p_file_format IN FND_LOBS.file_format%type default 'binary'
, p_file_url OUT varchar2)
IS
PRAGMA AUTONOMOUS_TRANSACTION;
lBlob BLOB;
lFile BFILE := BFILENAME(p_directory, p_file_name);
L_ORA_CHARSET VARCHAR2(100);
P_COUNT NUMBER;
BEGIN
SELECT value
into l_ora_charset
FROM nls_database_parameters
where parameter = 'NLS_CHARACTERSET';
insert into FND_LOBS
(
file_id
, file_name
, file_content_type
, file_data
, upload_date
, expiration_date
, program_name
, program_tag
, LANGUAGE
, oracle_charset
, file_format
)
values
(
fnd_lobs_s.NEXTVAL -- FILE_ID
, p_file_name -- FILE_NAME
, p_content_type -- FILE_CONTENT_TYPE
, EMPTY_BLOB() -- FILE_DATA
, sysdate -- UPLOAD_DATE
, NULL -- EXPIRATION_DATE
, p_program_name -- PROGRAM_NAME
, p_program_tag -- PROGRAM_TAG
, p_language -- LANGUAGE
, l_ora_charset -- ORACLE_CHARSET
, p_file_format -- FILE_FORMAT
)
RETURNING file_data INTO lBlob;
DBMS_LOB.OPEN(lFile, DBMS_LOB.LOB_READONLY);
DBMS_LOB.OPEN(lBlob, DBMS_LOB.LOB_READWRITE);
DBMS_LOB.LOADFROMFILE(DEST_LOB => lBlob,
SRC_LOB => lFile,
AMOUNT => DBMS_LOB.GETLENGTH(lFile));
DBMS_LOB.CLOSE(lFile);
DBMS_LOB.CLOSE(lBlob);
commit;
p_file_url := fnd_gfm.construct_download_url (fnd_web_config.gfm_agent, fnd_lobs_s.currval);
END IMPORT_TO_LOB;
Note that this is an AUTONOMOUS_TRANSACTION so it needs to be committed before returning to the calling package/block.
Hope that Helps!