Delphi IntraWeb has a component to manage file upload in async way. I would like to:
- Allow browser user to select a file;
- Test the file name client side (i.e. in order to verify some condition or info and ask user for supplementary info depending from selected file name). The JS procedure should be in charge to provide formatted info about the file name; the provided default info span is too small and I want to use different interface items (cards and classes), and the file name displayed is shortened;
- Execute upload: the "Upload" button will be shown only conditionally, controlled client side.
Looking to FileUploaderDemo.prj I found there are some useful client side script procedures from IntraWeb:
IW.FileUploader("IWFILEUPLOADER5").selectFile();
IW.FileUploader("IWFILEUPLOADER5").startUpload();
So I can control the selection and the upload processes client side as needed, but I need to fire my own event handler on selected file, and process file info.
I guess if others IntraWeb JS client side procedures are available, similar to:
*** EXAMPLE ***
IW.FileUploader("IWFILEUPLOADER5").selectedFileName(); // get the selected file name
IW.FileUploader("IWFILEUPLOADER5").selectedFileSize(); // get the selected file size
IW.FileUploader("IWFILEUPLOADER5").hideDefaultInfoPanel(); // hide the default info panel
And if a JS event exists in order to fire my own JS procedure when users select a file.
EDIT 28 jan 2023
After many attempts I found this solution valid for may goal, but still not THE solution: I would like to process the file name client side only, without sending info's to the server. Anyway I can process it server side.
Drop a TIWFileUploader into the TIWForm, disable AutoUpload property. Drop a select button and an upload button like these:
object btnSelect: TIWButton
Left = 257
Top = 155
Width = 121
Height = 25
Caption = 'Select File'
Color = clBtnFace
Font.Color = clNone
Font.Size = 10
Font.Style = []
FriendlyName = 'Select File'
ScriptEvents = <
item
EventCode.Strings = (
'IW.FileUploader("EUPLOADTT").selectFile();')
Event = 'onClick'
end>
TabOrder = 2
end
object btnUpload: TIWButton
Left = 257
Top = 190
Width = 121
Height = 25
Caption = 'Upload File'
Color = clBtnFace
Font.Color = clNone
Font.Size = 10
Font.Style = []
FriendlyName = 'IWButton1'
ScriptEvents = <
item
EventCode.Strings = (
'IW.FileUploader("EUPLOADTT").startUpload();')
Event = 'onClick'
end>
TabOrder = 3
end
Render "btnUpload" hidden, so the user do now see it (i.e. add class "d-none", "hidden", or any other supported client side to the IW tag).
Assign the server side event handler OnAsyncSelectFile similar to this:
procedure TfHome.eUploadTTAsyncSelectFile(Sender: TObject;
EventParams: TStringList);
begin
ttUploader.Init(EventParams.Values['FileName']);
ttUploader.Parse();
var j := ttUploader.AsJSon;
WebApplication.CallBackResponse.AddJavaScriptToExecuteAsCDATA(Format('uplCheck(%s);',[j.ToString]));
j.Free;
end;
This is fired after user selected a file (or dropped it into the upload area - not perfect IW behavior here, but it works)
The ttUploader object is mine: here I process the file name and return a JSON object containing info so the client side can show a customized form asking user to confirm upload.
In my original question I said I have to check client side only the file name, but real implementation required I have to check database in order to find what action I have di ask to the user (i.e.: create ticket; update ticket; etc...). So this workaround is perfect form my actual needs.
Client side uplCheck(info) function shows the form to the user, including an action button. Bind to action button to this event:
function uplOk() {
executeAjaxEvent("&clkMode="+ttMode, null, "TT.uplProc",false,null,false);
$("#uplForm").modal("hide");
}
The ttMode var contains the kind of action returned by the server after file name parsing (i.e.: create a ticket).
The executeAjaxEvent calls the registered callback IW function:
procedure TfHome.IWAppFormCreate(Sender: TObject);
begin
...
WebApplication.RegisterCallBack('TT.uplProc', uplProc);
...
end;
Server side:
procedure TfHome.uplProc(EventParams: TStringList);
begin
ttUploader.actionAfterLoad := EventParams.Values['clkMode'];
WebApplication.CallBackResponse.AddJavaScriptToExecuteAsCDATA(Format('uplOkPost();',[]));
end;
The server save the action required, then call uplOkPost() JS function client side:
function uplOkPost() {
$("#BTNUPLOAD").addClass("disabled").trigger("click");
}
Looking at my code just now, I thin I can call directly IW.FileUploader("EUPLOADTT").startUpload(); without having to call the click handler of the button, but I never tried it.
Calling this JS function, the file is loaded client side, and I saved before the action type to be performed on eUploadTTAsyncUploadSuccess event handler. Here, I assigned SaveFile := false; in order to manage the file as a stream:
function fileUpload(): string;
begin
Result := 'S';
var s := TMemoryStream.Create;
try
try
eUploadTT.SaveToStream(FileName, s);
var o := TJSONObject.Create;
//s.LoadFromFile(ttUploader.DownloadedFullFileName);
o.AddPair('fileData',EncodeBase64(s.Memory,s.Size));
o.AddPair('rootFileName',ttUploader.RootFileName);
o.AddPair('ttCod',ttUploader.codTT);
o.AddPair('ttStato',ttUploader.stato);
o.AddPair('ttAnno',TJSONNumber.Create(ttUploader.annoTT));
o := UserSession.ClkPadFileBrowse.PutTTData(o);
except on E: Exception do begin
Result := 'E';
ttUploader.lastError := Format('Error loading file - "%s"',[E.Message]);
end;
end;
finally
// Release the stream
s.Free;
end;
end;
I assigned eUploadTTAsyncUploadError, eUploadTTAsyncUploadSuccess in order to manage exception and work flow (user feedback).