Here is a sample procedure that creates tab delimited excel file on the server. The filename is supplied from the form.

CREATE OR REPLACE PROCEDURE nrs_export_data (v_business_id varchar2, filename varchar2)
IS

out_file UTL_FILE.File_Type;
linebuf VARCHAR2(1999);
directory_name varchar2(100) := '/devl/nbr/data/exports';
v_total number := 0;
v_database VARCHAR2(30);
v_db_desc VARCHAR2(255);
v_rep_url VARCHAR2(255);
v_ret_val VARCHAR2(2000);
v_export_dir VARCHAR2(30);
lv_error_text varchar2(255);

CURSOR export_cur IS
SELECT p1.prodno PRIM_Prodno,
e.serial_no PRIM_Serialno,
c1.code PRIM_Status,
c2.code PRIM_Status_Reason,
e.status_user_id PRIM_Status_Userid,
e.status_timestamp PRIM_Status_Time,
e.registration_date PRIM_Regist_Date,
e.device_invoice_found PRIM_Device_Invoice_Found,
e.custno PRIM_Atlas_Custno,
b.operating_unit PRIM_Business,
tc.sales_rep_name PRIM_Sales_Rep,
p2.prodno SEC_Prodno,
se.serial_no SEC_Serialno,
c3.code SEC_Status,
c4.code SEC_Status_Reason,
se.status_user_id SEC_Status_Userid,
se.status_timestamp SEC_Status_Time,
se.registration_date SEC_Regist_Date,
se.device_invoice_found SEC_Device_Invoice_Found,
ph.docno Physician_Docno,
ph.firstname Physician_First_Name,
ph.lastname Physician_Last_Name,
ph.clinic_name Clinic_Name,
ph.address_line1 Address_Line1,
ph.address_line2 Address_Line2,
ph.address_line3 Address_Line3,
ph.city City,
ph.state State,
ph.zip Zip,
c5.code Country,
ph.phy_phone Physician_Phone,
ph.clinic_phone Clinic_Phone,
ph.active Physician_Status,
c6.code Physician_Status_Reason,
ph.status_user_id Physician_Status_Userid,
ph.status_timestamp Physician_Status_Time
FROM nrs_equipment e,nrs_equipment_xref ex,
nrs_physician ph,
nrs_product p1,
nrs_product p2,
nrs_sales_rep tc,
nrs_business b,
nrs_codes c1,
nrs_codes c2,
nrs_codes c3,
nrs_codes c4,
nrs_codes c5,
nrs_secondary_equipment se,
nrs_codes c6
WHERE e.business_id=v_business_id
AND e.equipment_id = ex.equipment_id_primary(+)
AND ex.equipment_id_secondary = se.equipment_id(+)
AND se.product_id = p2.product_id(+)
AND se.status_code_id = c3.code_id(+)
AND se.status_reason_code_id = c4.code_id(+)
AND e.business_id = b.business_id
AND e.product_id = p1.product_id
AND e.physician_id = ph.physician_id
AND e.sales_rep_id = tc.sales_rep_id(+)
AND e.status_code_id = c1.code_id
AND e.status_reason_code_id = c2.code_id
AND ph.country_code_id = c5.code_id
AND ph.status_reason_code_id = c6.code_id(+);

BEGIN
begin
out_file := UTL_FILE.Fopen(directory_name, filename, 'w');
exception
when others then
dbms_output.put_line('Error opening file');
end;

linebuf:= 'PRIM_Prodno'||' '||
'PRIM_Serialno'||' '||
'PRIM_Status'||' '||
'PRIM_Status_Reason'||' '||
'PRIM_Status_Userid'||' '||
'PRIM_Status_Time'||' '||
'PRIM_Regist_Date'||' '||
'PRIM_Device_Invoice_Found'||' '||
'PRIM_Atlas_Custno'||' '||
'PRIM_Business'||' '||'PRIM_Sales_Rep'||' '||'SEC_Prodno'||' '||
'SEC_Serialno'||' '||'SEC_Status'||' '||'SEC_Status_Reason'||' '||
'SEC_Status_Userid'||' '||'SEC_Status_Time'||' '||
'SEC_Regist_Date'||' '||'SEC_Device_Invoice_Found'||' '||
'Physician_Docno'||' '||'Physician_First_Name'||' '||
'Physician_Last_Name'||' '||'Clinic_Name'||' '||
'Address_Line1'||' '||'Address_Line2'||' '||
'Address_Line3'||' '||'City'||' '||
'State'||' '||'Zip'||' '||
'Country'||' '||'Physician_Phone'||' '||
'Clinic_Phone'||' '||'Physician_Status'||' '||
'Physician_Status_Reason'||' '||
'Physician_Status_Userid'||' '||'Physician_Status_Time';
UTL_FILE.Put_Line(out_file, linebuf);
FOR export_rec in export_cur LOOP
dbms_output.put_line(export_rec.PRIM_Prodno||export_rec.PRIM_serialno);
linebuf := export_rec.PRIM_Prodno||' '||
export_rec.PRIM_Serialno||' '||
export_rec.PRIM_Status||' '||
export_rec.PRIM_Status_Reason||' '||
export_rec.PRIM_Status_Userid||' '||
to_char(export_rec.PRIM_Status_Time,'DD-MON-YYYY')||' '||
to_char(export_rec.PRIM_Regist_Date,'DD-MON-YYYY')||' '||
export_rec.PRIM_Device_Invoice_Found||' '||
export_rec.PRIM_Atlas_Custno||' '||
export_rec.PRIM_Business||' '||
export_rec.PRIM_Sales_Rep||' '||
export_rec.SEC_Prodno||' '||
export_rec.SEC_Serialno||' '||
export_rec.SEC_Status||' '||
export_rec.SEC_Status_Reason||' '||
export_rec.SEC_Status_Userid||' '||
to_char(export_rec.SEC_Status_Time,'DD-MON-YYYY')||' '||
to_char(export_rec.SEC_Regist_Date, 'DD-MON-YYYY')||' '||
export_rec.SEC_Device_Invoice_Found||' '||
export_rec.Physician_Docno||' '||
export_rec.Physician_First_Name||' '||
export_rec.Physician_Last_Name||' '||
export_rec.Clinic_Name||' '||
export_rec.Address_Line1||' '||
export_rec.Address_Line2||' '||
export_rec.Address_Line3||' '||
export_rec.City||' '||
export_rec.State||' '||
export_rec.Zip||' '||
export_rec.Country||' '||
export_rec.Physician_Phone||' '||
export_rec.Clinic_Phone||' '||
export_rec.Physician_Status||' '||
export_rec.Physician_Status_Reason||' '||
export_rec.Physician_Status_Userid||' '||
to_char(export_rec.Physician_Status_Time, 'DD-MON-YYYY');
v_total := v_total + 1;
UTL_FILE.Put_Line(out_file, linebuf);
END LOOP;
UTL_FILE.Fclose(out_file);
EXCEPTION
WHEN no_data_found THEN
UTL_FILE.Fclose(out_file);
WHEN OTHERS THEN
dbms_output.put_line('Error in UTL_FILE');
END nrs_export_data;