I have an Oracle Apex application which generates automated eMails. In Apex, the user inserts a JPG image into a rich text field. That image is saved into a CLOB field. When the stored procedure is called, it reads the JPG image and stores it into a local variable called l_image_clob. The program sends the embedded image (note: this an embedded image and it is not an eMail attachment) along with the rest of the eMail body to a list of users. That's all working fine.
Now I'm attempting to save the contents of the JPG image stored in l_image_clob to a JPG file on the windows server. The following code produces a file, named properly and the size is correct, but it isn't readable by the system. I get the error "this is not a valid bitmap file" when I try to open it with Microsoft Paint. How to I use utl_file to do this?
Here's the code which creates the file that is "not a valid bitmap file"
-- Create a file based on the content of l_image_clob
l_image_filename := 'image_' || p_event_pkey || '_' || i ||
'.' || l_image_ext;
l_file_handle := utl_file.fopen(l_dirname , l_image_filename, 'wb');
-- wb is write byte. This returns file handle
<<inner_loop>>
for i in 1 .. ceil( length( l_image_clob ) / chnksz )
loop
utl_file.put_raw( l_file_handle,
utl_raw.cast_to_raw( substr( l_image_clob, (i-1) * chnksz + 1, chnksz )));
utl_file.fflush(l_file_handle);
end loop inner_loop;
utl_file.fclose(l_file_handle);
Thanks for looking at this.
I found an answer. The Apex-initiated image was base64 encoded. Therefore I had to Decode it. Someone helped me with a procedure to do this. My modified code now looks like this:
-- Create a file based on the content of l_image_clob
l_image_filename := 'image_' || p_event_pkey || '_' || i ||
'.' || l_image_ext;
clob_base64_to_file(l_image_clob, l_dirname, l_image_filename);
The procedure that's called is as follows:
create or replace procedure clob_base64_to_file(
p_clob in clob,
p_dir in varchar2,
p_filename in varchar2
)
is
t_buffer varchar2(32767);
t_pos number := 1;
t_len number;
t_fh utl_file.file_type;
t_size number := nls_charset_decl_len( 32764,
nls_charset_id( 'char_cs' ) );
begin
t_fh := utl_file.fopen( p_dir, p_filename, 'wb', 32767 );
t_len := length( p_clob );
loop
exit when t_pos > t_len;
t_buffer := replace( replace( substr( p_clob, t_pos, t_size ),
chr(10) ), chr(13) );
t_pos := t_pos + t_size;
while t_pos <= t_len and mod( length( t_buffer ), 4 ) > 0
loop
t_buffer := t_buffer || replace( replace( substr( p_clob, t_pos, 1 ),
chr(10) ), chr(13) );
t_pos := t_pos + 1;
end loop;
utl_file.put_raw( t_fh,
utl_encode.base64_decode( utl_raw.cast_to_raw( t_buffer ) ) );
end loop;
utl_file.fclose( t_fh );
end;
When I call the clob_base64_to_file procedure it decodes the image and creates a file based on the directory and filename I supply in the call.
Related
I was appending to a .csv file with no problems.
Instead, I want to append to an .xlsx file, because I want to be able to make a nice looking table.
Unfortunately, upon opening the xlsx file, I am getting this message:
"Excel cannot open the file 'printlog.xls' because the file format or the file extension is not valid. Verify that the file has not been corrupted and that the file extension matches the format of the file."
It does not do this with a csv file.
There is no official documentation on the AHK website that says it is possible to FileAppend to .xlsx format, but there are plenty of examples online of people doing so.
Here is my code:
SetWorkingDir %A_ScriptDir%
ControlGetText, test, Edit10, TeamViewer
filename = %A_ScriptDir%\printlog.xlsx
FileAppend,
(
%test%,%test%,%test%,%test%
%test%,%test%,%test%,%test%
), %filename%
UPDATE: the first portion of this has been solved thanks to #user3419297's solution. I have a new problem listed below.
FilePath := A_ScriptDir "\printlog.xlsx" ; get file path
SetWorkingDir %A_ScriptDir% ; set working directory
ControlGetText, itemcode, Edit1, Print - ; get item code from print menu
ControlGetText, quantity, Edit2, Print - ; get quantity from print menu
ControlGetText, amount, Edit3, Print - ; get amount from print menu
ControlGetText, initials, Edit4, Print - ; get quantity from print menu
ControlGetText, info, Edit5, Print - ; get quantity from print menu
ControlGetText, batch, Edit6, Print - ; get quantity from print menu
Formattime,ts,,Shortdate ; generate short date variable
oExcel := ComObjCreate("Excel.Application") ; create Excel COM object
oWorkbook := oExcel.Workbooks.Open(FilePath) ; open workbook in Excel object
oExcel.Visible := false ; work in background
; check for blank rows and append values
For Row in oExcel.Range["A1"] ; starting with the first row
{
If (Row.Value = "") ; if first row is blank
{
oExcel.Range("A1").Value := %itemcode% ; set value of cell A-G
oExcel.Range("B1").Value := %quantity%
oExcel.Range("C1").Value := %amount%
oExcel.Range("D1").Value := %initials%
oExcel.Range("E1").Value := %info%
oExcel.Range("F1").Value := %batch%
oExcel.Range("G1").Value := %ts%
}
else ; else find the next blank row and set values of A-G
For eachRow in oExcel.Range["A1:A" oExcel.Columns("A").Find[""].Row]
{
If (eachRow.Value = "")
{
oExcel.Range("A" . A_Index).Value := %itemcode%
oExcel.Range("B" . A_Index).Value := %quantity%
oExcel.Range("C" . A_Index).Value := %amount%
oExcel.Range("D" . A_Index).Value := %initials%
oExcel.Range("E" . A_Index).Value := %info%
oExcel.Range("F" . A_Index).Value := %batch%
oExcel.Range("G" . A_Index).Value := %ts%
break
}
}
}
oWorkbook.Save() ; save workbook and close excel
oExcel.Quit()
as you can see the error that I am getting is an illegal character. I don't understand because the contents of the variable is the string. Does this have something to do with the format of the variable in itself, or is it the format in which it is being appended to the excel file?
To append text to an .xlsx file, you have to use COM:
test := "my text"
FilePath := A_ScriptDir "\printlog.xlsx"
oExcel := ComObjCreate("Excel.Application")
oWorkbook := oExcel.Workbooks.Open(FilePath)
oExcel.Visible := false
oExcel.Range("A1").Value := test
oWorkbook.Save()
oExcel.Quit()
Another example:
Append the first 3 words of the copied text to the next empty row in their respective columns a,b,c:
FilePath := A_ScriptDir "\printlog.xlsx"
Array:=[]
Array := StrSplit(clipboard, " ")
oExcel := ComObjCreate("Excel.Application")
oWorkbook := oExcel.Workbooks.Open(FilePath)
oExcel.Visible := false
For Row in oExcel.Range["A1"]
{
If (Row.Value = "")
{
oExcel.Range("A1").Value := Array[1]
oExcel.Range("B1").Value := Array[2]
oExcel.Range("C1").Value := Array[3]
}
else
For eachRow in oExcel.Range["A1:A" oExcel.Columns("A").Find[""].Row]
{
If (eachRow.Value = "")
{
oExcel.Range("A" . A_Index).Value := Array[1]
oExcel.Range("B" . A_Index).Value := Array[2]
oExcel.Range("C" . A_Index).Value := Array[3]
break
}
}
}
oWorkbook.Save()
oExcel.Quit()
For your second problem with the screenshot it is easy, as I made this one a lot of time...
This :
oExcel.Range("A1").Value := %itemcode%
Means : take the value of the variable which the name is the content of the variable itemcode ("this. is. a. test." I guess ?).
It cannot work in your case.
Example :
itemcode := "A B"
msgbox %itemcode% ; display will be : A B
So this :
Test := %itemcode%
is the same as :
Test = %A B%
Not compiling. :-)
So you need to remove the % % unless you have dynamic variables (rarely the case) so you store their name in an other variable (varception ?).
oExcel.Range("A1").Value := itemcode
I'm importing data from Excel to database table using Oracle Forms. Here is the code I'm using:
ligne_fin := 300;
FOR Z in 2..ligne_fin LOOP
ligne:=Z;
v_societe := Excel.get(1,Z,1) ;
v_compte := Excel.get(1,Z,2);
v_Tiers := Excel.get(1,Z,3);
v_section := Excel.get(1,Z,4);
insert into EntrCl (SOCIETE,
COMPTE,
TIERS,
SECTION)
VALUES (v_societe,
v_compte,
v_Tiers,
v_section);
END LOOP
With my get function
function GET(FOLIO in NUMBER, prow in number,pcol in number)
RETURN varchar2
is
deger varchar2(800);
Begin
deger:='';
args := OLE2.create_arglist;
OLE2.add_arg (args, FOLIO);
Worksheet:=OLE2.GET_OBJ_PROPERTY(workbook,'Worksheets',args);
args:=OLE2.CREATE_ARGLIST;
OLE2.ADD_ARG(args, prow);
OLE2.ADD_ARG(args, pcol);
cell:=ole2.get_obj_property(worksheet, 'Cells', args);
deger := OLE2.Get_Char_Property(cell, 'Text');
OLE2.RELEASE_OBJ(cell);
OLE2.destroy_arglist (args);
return(deger);
end;
My issue is that I want to get my ligne_fin automatically using code. I don't want to hardcode it.
Is there any way to get the last record of Excel file using PL/SQL ?
Check if a column returned null value to identify last row
Z:=2;
While(Excel.get(1,Z,1) is not null)
loop
v_societe := Excel.get(1,Z,1) ;
v_compte := Excel.get(1,Z,2);
v_Tiers := Excel.get(1,Z,3);
v_section := Excel.get(1,Z,4);
insert into EntrCl (SOCIETE,
COMPTE,
TIERS,
SECTION)
VALUES (v_societe,
v_compte,
v_Tiers,
v_section);
Z:=Z+1;
end loop;
I need to extract all email addresses from this website:
http://www.danskeark.dk/Medlemsindex.aspx
To navigate to the addresses go to letter A,B,C,D... and then by company.
I also need to export the found addresses to excel.
How do I do that the easiest way?
mirror the site with wget in a new dir
wget -mk --domains danskeark.dk danskeark.dk
grep all mail adresses out to a csv in parent dir in that dir
find . | xargs grep -E -o "\b[a-zA-Z0-9.-]+#[a-zA-Z0-9.-]+\.[a-zA-Z0-9.-]+\b" > ../out.csv
Here is a little crawler made with ahk (Free open-source scripting language for Windows)
So you will need to download that from the link above
I used a visible IE object to keep what its doing open, makes it a bit slow (5-7 mins) but hey if you only need it once...
url := "http://www.danskeark.dk/Medlemsindex.aspx"
wb := ComObjCreate("InternetExplorer.Application")
wb.visible := true
virksomheds_Urls := []
chars := "ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ0123456789"
loop, parse, chars
{
index := "?index=" A_LoopField
wb.Navigate(url . index)
while wb.readyState!=4 || wb.document.readyState != "complete" || wb.busy
continue
pages := wb.document.getElementById("pagesTop").getElementsByTagName("A").length - 1
loop % pages
{
wb.Navigate(url . index . "&pg=" A_index)
while wb.readyState!=4 || wb.document.readyState != "complete" || wb.busy
continue
loop % (links := wb.document.getElementsByTagName("UL")[1].getElementsByTagName("A")).length
{
virksomheds_Urls.Insert(links[A_index-1].href)
}
}
}
for, key, val in virksomheds_Urls
{
wb.Navigate(val)
while wb.readyState!=4 || wb.document.readyState != "complete" || wb.busy
continue
csv .= (Email := wb.document.getElementById("divContactBox").GetelementsByTagName("A")[0].innertext) ","
}
FileAppend, %csv%, Emails_csv.csv
run, excel.exe Emails_csv.csv
return
I'm trying to make use of the TOpenDialog in order to pass the path to selected file to the AdoConection and load the content of the Excel file to the table. I'm currently attempting the code below but the last part of the code does not connect to the Excel returning an error:
[dcc32 Error] sample_map.pas(80): E2010 Incompatible types: 'string' and 'TOpenDialog'
procedure TForm1.Button1Click(Sender: TObject);
var
openDialog : TOpenDialog; // Open dialog variable
strConn : WideString; // Declare wide string for the connection
begin
// Create the open dialog object - assign to our open dialog variable
openDialog := TOpenDialog.Create(self);
// Set up the starting directory to be the current one
openDialog.InitialDir := GetCurrentDir;
// Only allow existing files to be selected
openDialog.Options := [ofFileMustExist];
// Allow only .dpr and .pas files to be selected
openDialog.Filter :=
'Excel 2003 and older|*.xls|Excel 2007 and older|*.xlsx';
// Select pascal files as the starting filter type
openDialog.FilterIndex := 2;
// Display the open file dialog
if openDialog.Execute
then ShowMessage('File : '+openDialog.FileName)
else ShowMessage('Open file was cancelled');
// Free up the dialog
openDialog.Free;
// Connect the Excel file
strConn:='Provider=Microsoft.Jet.OLEDB.4.0;' +
'Data Source=' + openDialog + ';' +
'Extended Properties=Excel 8.0;';
AdoConnection1.Connected:=False;
AdoConnection1.ConnectionString:=strConn;
end;
openDialog is an instance of a file dialog. It is not a string. You need to read the FileName property of the file dialog object like this:
openDialog.FileName
In fact you already use that in one of your ShowMessage calls.
Do make sure that you read this property before calling Free, a mistake present in the code in the question.
In fact you do need to get in to the habit of using try/finally to protect resources. Any time you create an instance of a class you need to make sure that it will be destroyed even in the face of an exception. In your code you need to write it like this:
openDialog := TOpenDialog.Create(self);
try
.... // use openDialog to let user choose file:
strConn := 'Provider=Microsoft.Jet.OLEDB.4.0;' +
'Data Source=' + openDialog.FileName + ';' +
'Extended Properties=Excel 8.0;';
finally
openDialog.Free; // executes no matter what, even if exception raised, etc.
end;
I also don't think you need to use WideString here. If you use a Unicode Delphi then you can use the native string type which is an alias for UnicodeString. If your Delphi is pre-Unicode, then you can also safely use string, an alias for AnsiString in that case. The literals you use are ASCII. The file dialog is an ANSI control and so openDialog.FileName is also ANSI. Nothing to be gained using WideString.
Finally, you are mixing up, all in one function, code to select a filename, and code to work on a database connection. It is better to separate concerns. Create a method that simply returns a filename, obtained by letting the user choose through a dialog. And add a method to work on the database connection, that is passed a filename as a parameter.
You need to get the OpenDialog.FileName prior to freeing the dialog:
OpenDialog := TOpenDialog.Create(nil);
try
// Set up the OpenDialog as before
// Display the open file dialog
if openDialog.Execute then
begin
strConn := 'Provider=Microsoft.Jet.OLEDB.4.0;' +
'Data Source=' + openDialog.FileName + ';' +
'Extended Properties=Excel 8.0;';
// Connect the Excel file
AdoConnection1.Connected:=False;
AdoConnection1.ConnectionString:=strConn;
else
ShowMessage('Open file was cancelled');
finally
// Free up the dialog
openDialog.Free;
end;
Of course, you're working much too hard. The Dialogs unit has a much easier way to do this using the PromptForFilename function, which eliminates the need to create and free the dialog entirely:
var
FileName: string;
begin
FileName := '';
if PromptForFileName(FileName, // Chosen filename holder
'Excel 2003 and older|*.xls|Excel 2007 and older|*.xlsx'; // Filter(s) (optional)
'.xlsx', // Default extension (opt)
'Choose file', // Dialog title (opt)
GetCurrentDir, // Initial dir (opt)
False) then // Is it a save dlg? (opt)
begin
strConn := 'Provider=Microsoft.Jet.OLEDB.4.0;' +
'Data Source=' + FileName + ';' +
'Extended Properties=Excel 8.0;';
// Connect the Excel file
AdoConnection1.Connected:=False;
AdoConnection1.ConnectionString:=strConn;
end
else
ShowMessage('Dialog cancelled.');
end;
As a side note, in case you don't know: You can select all Excel files (both .xls and .xlsx) with a single filter if you enter it as .xls*, as in Excel Files|*.xls*.
And, as David says, the best way would be to separate out the code that gets the filename and the code that does the connection as separate functions. Call the first to get the filename, and then pass that filename to the second to actually do the connecting if a filename is selected.
I’m trying to replace a "comma" with "comma + space" using the following code in a procedure called by the OnChange Event on a RichEdit control in Delphi 2010.
SomeString := RichEdit.Lines.Strings[RichEdit.Lines.Count-1];
Position := Pos(',', SomeString);
if Position > 0 then
begin
Delete(SomeString, Position, 1);
Insert(', ', SomeString, Position);
RichEdit.Lines.Strings[RichEdit.Lines.Count-1] := SomeString;
end;
It works perfectly, but I can’t use BACKSPACE and DEL via keyboard anymore (on the RichEdit Control), because the inserted characters act like barriers. It doesn’t happen with another set of inserted characters, only with "comma + space".
Can someone tell me what am I doing wrong here ?
Just try this code
//get the string
SomeString := RichEdit.Lines.Strings[RichEdit.Lines.Count-1];
//replace the 'comma' with 'comma+space'
SomeString :=StringReplace(SomeString ,',',', ',[rfReplaceAll,rfIgnoreCase]);
//now delete the extra space which gets added each time you call this event
SomeString :=StringReplace(SomeString ,', ',', ',[rfReplaceAll,rfIgnoreCase]);
//your desired result
RichEdit.Lines.Strings[RichEdit.Lines.Count-1] := SomeString ;
Remember the BACKSPACE and DEL are working fine. But in case of your code there was an extra space added each time you try and change the contents of RichEdit. Hence giving an impression of the 2 keys not working.
Since you are having problems deleting the space in front of comma I will provide with another solution
Add a Boolean variable to the class isComma: Boolean.
Later on OnKeyPress event of RichEdit inset this code
isComma := False;
if key = ',' then
isComma := True;
OnChange event code goes here
var
CurPoint: TPoint;
cText: string;
cLine: string;
begin
if isComma then
begin
cLine:=' '; //character to be added after comma, ' ' in here
CurPoint:=RichEdit1.CaretPos;
cText:=RichEdit1.Lines.Strings[CurPoint.y];
//add ' ' where the cursor is i.e. after the comma
cText:=Copy(cText,0,CurPoint.x)+cLine+Copy(cText,CurPoint.x+1,Length(cText));
RichEdit1.Lines.Strings[CurPoint.y]:=cText;
end;
end;