Web application developers on Caché and Ensemble often have the task of “file upload” - downloading files from a browser. Recently, the Caché forum on
SQL.ru again raised several questions about how to do background file downloads. I decided to describe how this can be done using CSP and ZEN technologies.
There are a lot of options for ready-made available components for the implementation of this task. All of them differ in the available breadth of functionality, as well as support for browsers and technologies. Most of them mainly use the HTML element
/>
. There are those in which the download occurs using Flash. With the beginning of support for FileAPI from HTML5 browsers, it became possible to find ready-made JavaScript components and with the support of this technology.
To implement the example, one of the ready-made scripts found on the Internet for downloading files to the server,
FineUploader, was chosen . For the visual design, the
Bootstrap framework was chosen. In the component chosen by me, there is support for loading several Drag-n-Drop files, but there is no support for FileAPI. When downloading multiple files, progress is displayed, with the status of each downloaded file.
Download FineUploader and Bootstrap, unpack to the root of the folder of our CSP-application. For example, for the
USER
domain, the CSP application will be
/csp/user
by default, when installing Caché, the default path will be
C:\InterSystems\Cache\CSP\user
.
Implementation example on CSP.Class habr.CSPFileUpload Extends% CSP.Page
{
ClassMethod OnPage () As% Status
{
#; The file has been transmitted, so we will respond to the browser that we have received the file and how
if $ d ( % request . Data ( "loadFile" )) {
if $ lv ( % loadFileSuccess ), $ lg ( % loadFileSuccess , 1) {
#; The file was received successfully, we will answer about the successful reception and send the name of the file and its size.
w "{" "success" ": true," ,
"" "fileName" ":" "" _ $ lg ( % loadFileSuccess , 2) _ "" "," ,
"" "fileSize" ":" "" _ $ lg ( % loadFileSuccess , 3) _ "" "}"
} else {
#; An error occurred while receiving the file
w "{" "error" ":" "Error load file" "}"
}
q $$$ OK
}
#; HTML form for sending files
& html << ! DOCTYPE HTML>
< html >
<! - Connect the Fine Uploader style ->
< link href = "fileuploader.css" rel = "stylesheet" type = "text / css" >
<! - Bootstrap styles, for visual design ->
< link href = "bootstrap / css / bootstrap.min.css" rel = "stylesheet" type = "text / css" >
< link href = "bootstrap / css / bootstrap-responsive.min.css" rel = "stylesheet" type = "text / css" >
< body >
<! - This will be the field for the file ->
< div id = "thumbnail-fine-uploader" style = 'width: 400px' > </ div >
')
<! - jQuery ->
< script type = "text / javascript" src = "jquery.js" > </ script >
<! - script Fine Uploader ->
< script type = "text / javascript" src = "fileuploader.js" > </ script >
<! - Bootstrap script ->
< script type = "text / javascript" src = "bootstrap / js / bootstrap.min.js" > </ script >
< script type = "text / javascript" >
// Run Fine Uploader
$ (document) .ready ( function () {
var thumbnailuploader = new qq.FileUploader ({
element: $ ( '# thumbnail-fine-uploader' ) [ 0 ], // our element
// here, we specify the URL of the file uploader page, in this case it is the current class
action: '# ( .. % ClassName (1) _ ".cls" ) #' ,
// Additional parameters passed with the file
params: {
loadFile: 1
},
// Allow downloading multiple files
multiple: true
// Valid file types
allowedExtensions: [ 'jpeg' , 'jpg' , 'gif' , 'png' ],
// File size limit
sizeLimit: 5120000 ,
onComplete: function (id, fileName, responseJSON) {
if (responseJSON.success) {
// File successfully sent, add information
$ ( '# thumbnail-fine-uploader' )
.append ( '<div> <span> Loaded File:' + responseJSON.fileName + '</ span>' +
'<span> File size:' + responseJSON.fileSize + '</ span> </ div>' );
}
}
});
});
</ script >
</ body >
</ html >>
Quit $$$ OK
}
ClassMethod OnPreHTTP () As% Boolean [ ServerOnly = 1]
{
#; File transferred
if $ d ( % request . Data ( "loadFile" )) {
s % loadFileSuccess = 0
#; file field
s FieldName = "qqfile"
#; get the file contents
i % request . IsDefinedMimeData ( FieldName , 1) {
s ContentType = % request . MimeData ( FieldName , 1). ContentType
s Content = % request . MimeData ( FieldName , 1)
} else {
s ContentType = % request . ContentType
s Content = % request . Content
}
s : FieldName '= "" fileName = $ g ( % request . Data ( FieldName , 1))
#; If the file is received, and it is not of zero size, we will collect information on it.
i $ isobject ( Content ), Content . Size > 0 s % loadFileSuccess = $ lb (1, $ g ( fileName ), Content . Size )
q 1
}
q 1
}
}
Below is an example using ZEN technology. The example uses only one page for displaying the file upload form, and for uploading the file to the server itself and returning the status.
ZEN example/// Created using the page template: Default
Class habr.ZENFileUpload Extends% ZEN.Component.page
{
/// Display name for the new application.
Parameter PAGENAME = "Test FileUploader" ;
/// Styles
Parameter CSSINCLUDES As STRING = "fileuploader.css, bootstrap / css / bootstrap.min.css, bootstrap / css / bootstrap-responsive.min.css" ;
/// JS files
Parameter JSINCLUDES As STRING = "jquery.js, fileuploader.js, bootstrap / js / bootstrap.min.js" ;
/// This XML block describes the content of this page.
XData Contents [ XMLNamespace = " www.intersystems.com/zen" ]
{
< page xmlns = " www.intersystems.com/zen" title = "" >
<! - Block to display file upload field ->
< pane id = "thumbnail-fine-uploader" width = "400px" />
</ page >
}
/// After loading the page, run the component
ClientMethod onloadHandler () [ Language = javascript]
{
var thumbnailuploader = new qq.FileUploader ({
element: $ ( '# thumbnail-fine-uploader' ) [ 0 ], // our element
// here, we specify the URL of the file uploader page, in this case it is the current class
action: '?' ,
// Additional parameters passed with the file
params: {
loadFile: 1
},
// Allow downloading multiple files
multiple: true
// Valid file types
allowedExtensions: [ 'jpeg' , 'jpg' , 'gif' , 'png' ],
// File size limit
sizeLimit: 5120000 ,
onComplete: function (id, fileName, responseJSON) {
if (responseJSON.success) {
// File successfully sent, add information
$ ( '# thumbnail-fine-uploader' )
.append ( '<div> <span> Loaded File:' + responseJSON.fileName + '</ span>' +
'<span> File size:' + responseJSON.fileSize + '</ span> </ div>' );
}
}
});
}
/// Zen page notification of an HTTP request. This method can be overwritten
/// by subclasses. <br/>
/// This is the standard Zen pre-HTTP processing occurs.
ClassMethod % OnPreHTTP () As% Boolean [ ServerOnly = 1]
{
#; File transferred
if $ d ( % request . Data ( "loadFile" )) {
s % loadFileSuccess = 0
#; file field
s FieldName = "qqfile"
#; get the file contents
i % request . IsDefinedMimeData ( FieldName , 1) {
s ContentType = % request . MimeData ( FieldName , 1). ContentType
s Content = % request . MimeData ( FieldName , 1)
} else {
s ContentType = % request . ContentType
s Content = % request . Content
}
s : FieldName '= "" fileName = $ g ( % request . Data ( FieldName , 1))
#; If the file is received, and it is not of zero size, we will collect information on it.
i $ isobject ( Content ), Content . Size > 0 s % loadFileSuccess = $ lb (1, $ g ( fileName ), Content . Size )
q 1
}
q 1
}
/// To process the file download, override the paint drawing method.
Method % DrawHTMLPage ()
{
#; If there was a file upload to the server, we’ll return the upload status in JSON format
if $ d ( % request . Data ( "loadFile" )) {
if $ lv ( % loadFileSuccess ), $ lg ( % loadFileSuccess , 1) {
#; The file was received successfully, we will answer about the successful reception and send the name of the file and its size.
w "{" "success" ": true," ,
"" "fileName" ":" "" _ $ lg ( % loadFileSuccess , 2) _ "" "," ,
"" "fileSize" ":" "" _ $ lg ( % loadFileSuccess , 3) _ "" "}"
} else {
#; An error occurred while receiving the file
w "{" "error" ":" "Error load file" "}"
}
q
}
#; Otherwise, draw the default page
d ## super ()
}
/// Disable the output of the page formation time at the end
Parameter SHOWSTATS As BOOLEAN = 0 ;
} Examples of use are perfectly presented on the
FineUploader website - there is both an example of downloading a single file, and working with several files, Drag & Drop, outputting download progress.
Sources for the proposed examples without the Bootstrap and Fine Uplaoder files