Реализация фоновой загрузки файлов на сервер Caché

У разработчиков веб-приложений на Caché и Ensemble часто возникает задача “file upload” – загрузки файлов с браузера. Недавно на форуме по Caché на SQL.ru снова возникло несколько вопросов о том, как сделать фоновую загрузку файлов. Решил описать как это можно сделать с использованием технологий CSP и ZEN.

Вариантов готовых доступных компонент, для реализации данной задачи достаточно много. Все они отличаются доступной широтой функционала, а так же поддержкой браузеров и технологий. В большинстве из них в основном используется HTML элемент

<input type='file'/>

. Есть такие в которых загрузка происходит с помощью Flash. С началом поддержки FileAPI из HTML5 браузерами, стало возможным найти готовые JavaScript компоненты и с поддержкой данной технологии.

Для реализации примера был выбран один из найденных на просторах интернета готовых скриптов для загрузки файлов на сервер – FineUploader. Для визуального оформления был выбран фреймворк Bootstrap. В выбранной мной компоненте, имеется поддержка загрузки нескольких файлов Drag-n-Drop, но отсутствует поддержка FileAPI. При загрузке нескольких файлов отображается прогресс, с отображением статуса по каждому загружаемому файлу.
Скачиваем FineUploader и Bootstrap, распаковываем в корень папки нашего CSP-приложения. К примеру для области

USER

, CSP-приложение по умолчанию будет

/csp/user

, при установке Caché по умолчанию путь будет

C:\InterSystems\Cache\CSP\user

.
Пример реализации на CSP.
Class habr.CSPFileUpload Extends %CSP.Page
{

ClassMethod OnPage() As %Status
{
  
#; был передан файл, поэтому ответим браузеру, о том что приняли файл и как
  
if $d(%request.Data("loadFile")) {
    
if $lv(%loadFileSuccess),$lg(%loadFileSuccess,1) {
      
#; файл принят удачно, ответим об удачном приеме и отправим имя файла и его размер
      
"{""success"":true,",
        
"""fileName"":"""_$lg(%loadFileSuccess,2)_""",",
        
"""fileSize"":"""_$lg(%loadFileSuccess,3)_"""}"
    
else {
      
#; произошла ошибка при приеме файла
      
"{""error"":""Error load file""}"
    
}
    
q $$$OK
  
}
  
#; HTML форма для отправки файлов
  
&html<<!DOCTYPE HTML>
<
html>
<!–
 Подключим стиль Fine Uploader –>
<
link href="fileuploader.css" rel="stylesheet" type="text/css">
<!–
 Стили Bootstrap, для визуального оформления –>
<
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>
<!–
 Это будет полем для файла –>
<
div id="thumbnail-fine-uploader" style=‘width:400px’ ></div>

<!– jQuery –>
<
script type="text/javascript" src="jquery.js"></script>
<!–
 скрипт Fine Uploader –>
<
script type="text/javascript" src="fileuploader.js"></script>
<!–
 скрипт Bootstrap –>
<
script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
<
script type="text/javascript" >
// Запустим Fine Uploader
$(document).ready(function() {
    
var thumbnailuploader new qq.FileUploader({
      element: $(
‘#thumbnail-fine-uploader’)[0],  // наш элемент
      // здесь, укажем адрес страницы загрузичика файла, в данном случае это текущий класс
      
action: ‘#(..%ClassName(1)_".cls")#’,       
      
// Дополнительные параметры, передаваемые вместе с файлом
      
params: {
        loadFile: 
1
        
},
    
// Разрешим загрузку нескольких файлов
      
multiple: true,
      
// Допустимые типы файлов
      
allowedExtensions: [‘jpeg’‘jpg’‘gif’‘png’],
      
// Ограничение  размера файлов
      
sizeLimit: 5120000,
      onComplete: 
function(id, fileName, responseJSON) {
        
if (responseJSON.success) {
          
// Файл успешно отправлен, добавим информацию
            
$(‘#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 ]
{
  
#; Передан файл
  
if $d(%request.Data("loadFile")) {
    
%loadFileSuccess=0
    
#; поле с файлом
    
FieldName="qqfile"
    #; получим содержимое файла
    
%request.IsDefinedMimeData(FieldName,1) {
      
ContentType=%request.MimeData(FieldName,1).ContentType
      s 
Content=%request.MimeData(FieldName,1)
    
else {
      
ContentType=%request.ContentType
      s 
Content=%request.Content
    
}
    
s:FieldName‘="" fileName=$g(%request.Data(FieldName,1))
    
#; если файл получен, и он не нулевого размера, соберем информацию по нему
    
i $isobject(Content),Content.Size>0 %loadFileSuccess=$lb(1,$g(fileName),Content.Size)
    
1
  
}
  
1
}

}

Ниже представлен пример с использованием технологии ZEN. В примере используется только одна страница как для отображения формы загрузки файла, так и для самой загрузки файла на сервер и возврата статуса.
Пример на ZEN
/// Created using the page template: Default
Class habr.ZENFileUpload Extends %ZEN.Component.page
{

/// Отображаемое имя для нового приложения.
Parameter 
PAGENAME = "Test FileUploader";

/// Стили
Parameter 
CSSINCLUDES As STRING = "fileuploader.css,bootstrap/css/bootstrap.min.css,bootstrap/css/bootstrap-responsive.min.css";

/// JS-файлы
Parameter 
JSINCLUDES As STRING = "jquery.js,fileuploader.js,bootstrap/js/bootstrap.min.js";

/// Этот XML блок описывает содержимое этой страницы.
XData 
Contents [ XMLNamespace "http://www.intersystems.com/zen" ]
{
<
page xmlns="http://www.intersystems.com/zen" title="">
<!– Блок для отображения поля для загрузки файла –>
<pane id="thumbnail-fine-uploader" width="400px"/>
</
page>
}

/// После загрузки страницы, запустим компоненту
ClientMethod 
onloadHandler() [ Language = javascript ]
{
    
var thumbnailuploader new qq.FileUploader({
      element: $(
‘#thumbnail-fine-uploader’)[0],  // наш элемент
      // здесь, укажем адрес страницы загрузичика файла, в данном случае это текущий класс
      
action: ‘?’,
      
// Дополнительные параметры, передаваемые вместе с файлом
      
params: {
        loadFile: 
1
        
},
    
// Разрешим загрузку нескольких файлов
      
multiple: true,
      
// Допустимые типы файлов
      
allowedExtensions: [‘jpeg’‘jpg’‘gif’‘png’],
      
// Ограничение  размера файлов
      
sizeLimit: 5120000,
      onComplete: 
function(id, fileName, responseJSON) {
        
if (responseJSON.success) {
          
// Файл успешно отправлен, добавим информацию
            
$(‘#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 called <em>before</em> the standard Zen pre-HTTP processing occurs.
ClassMethod 
%OnPreHTTP() As %Boolean ServerOnly = 1 ]
{
  
#; Передан файл
  
if $d(%request.Data("loadFile")) {
    
%loadFileSuccess=0
    
#; поле с файлом
    
FieldName="qqfile"
    #; получим содержимое файла
    
%request.IsDefinedMimeData(FieldName,1) {
      
ContentType=%request.MimeData(FieldName,1).ContentType
      s 
Content=%request.MimeData(FieldName,1)
    
else {
      
ContentType=%request.ContentType
      s 
Content=%request.Content
    
}
    
s:FieldName‘="" fileName=$g(%request.Data(FieldName,1))
    
#; если файл получен, и он не нулевого размера, соберем информацию по нему
    
i $isobject(Content),Content.Size>0 %loadFileSuccess=$lb(1,$g(fileName),Content.Size)
    
1
  
}
  
1
}

/// Для обработки загрузки файла переопределим метод рисования стрицы
Method 
%DrawHTMLPage()
{
  
#; Если была загрузка файла на сервер, вернем статус загрузки в формате JSON
  
if $d(%request.Data("loadFile")) {
    
if $lv(%loadFileSuccess),$lg(%loadFileSuccess,1) {
      
#; файл принят удачно, ответим об удачном приеме и отправим имя файла и его размер
      
"{""success"":true,",
        
"""fileName"":"""_$lg(%loadFileSuccess,2)_""",",
        
"""fileSize"":"""_$lg(%loadFileSuccess,3)_"""}"
    
else {
      
#; произошла ошибка при приеме файла
      
"{""error"":""Error load file""}"
    
}
    
q
  
}
  
#; Иначе отрисуем страницу по умолчанию
  
##super()
}

/// Отключим вывод времени формирования страницы в конце страницы 
Parameter 
SHOWSTATS As BOOLEAN = 0;

}
Примеры использования отлично представлены на сайте FineUploader – там есть как пример загрузки отдельного файла, так и работа с несколькими файлами, Drag & Drop, вывод прогресса загрузки.
Исходники предложенных примеров без файлов Bootstrap и Fine Uplaoder

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *