У разработчиков веб-приложений на 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:InterSystemsCacheCSPuser
.
{ ClassMethod OnPage() As %Status
{
#; был передан файл, поэтому ответим браузеру, о том что приняли файл и как
if $d(%request.Data("loadFile")) {
if $lv(%loadFileSuccess),$lg(%loadFileSuccess,1) {
#; файл принят удачно, ответим об удачном приеме и отправим имя файла и его размер
w "{""success"":true,",
"""fileName"":"""_$lg(%loadFileSuccess,2)_""",",
"""fileSize"":"""_$lg(%loadFileSuccess,3)_"""}"
} else {
#; произошла ошибка при приеме файла
w "{""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")) {
s %loadFileSuccess=0
#; поле с файлом
s FieldName="qqfile"
#; получим содержимое файла
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))
#; если файл получен, и он не нулевого размера, соберем информацию по нему
i $isobject(Content),Content.Size>0 s %loadFileSuccess=$lb(1,$g(fileName),Content.Size)
q 1
}
q 1
}
}
Ниже представлен пример с использованием технологии ZEN. В примере используется только одна страница как для отображения формы загрузки файла, так и для самой загрузки файла на сервер и возврата статуса.
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 = "www.intersystems.com/zen" ]
{
<page xmlns="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")) {
s %loadFileSuccess=0
#; поле с файлом
s FieldName="qqfile"
#; получим содержимое файла
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))
#; если файл получен, и он не нулевого размера, соберем информацию по нему
i $isobject(Content),Content.Size>0 s %loadFileSuccess=$lb(1,$g(fileName),Content.Size)
q 1
}
q 1
} /// Для обработки загрузки файла переопределим метод рисования стрицы
Method %DrawHTMLPage()
{
#; Если была загрузка файла на сервер, вернем статус загрузки в формате JSON
if $d(%request.Data("loadFile")) {
if $lv(%loadFileSuccess),$lg(%loadFileSuccess,1) {
#; файл принят удачно, ответим об удачном приеме и отправим имя файла и его размер
w "{""success"":true,",
"""fileName"":"""_$lg(%loadFileSuccess,2)_""",",
"""fileSize"":"""_$lg(%loadFileSuccess,3)_"""}"
} else {
#; произошла ошибка при приеме файла
w "{""error"":""Error load file""}"
}
q
}
#; Иначе отрисуем страницу по умолчанию
d ##super()
} /// Отключим вывод времени формирования страницы в конце страницы
Parameter SHOWSTATS As BOOLEAN = 0;
}
Примеры использования отлично представлены на сайте FineUploader — там есть как пример загрузки отдельного файла, так и работа с несколькими файлами, Drag & Drop, вывод прогресса загрузки.
Исходники предложенных примеров без файлов Bootstrap и Fine Uplaoder
Автор: DAiMor