Предлагаю Вашему вниманию статью на следующие на темы:
- Работа с FTP сервером с помощью %Net.FtpSession
- Простой способ выгрузки данных в формат xls
- Несколько полезных советов
Работа с FTP сервером.
Вебсервисы? Не, не слышали.
Третьего дня нашей компании довелось организовывать потоковую синхронизацию данных с производственным комплексом «Хэ». IT отдел заказчика настаивал на использовании FTP сервера, другие способы обмена яростно отвергались.
Нам понадобятся:
s ftp = ##class(%Net.FtpSession).%New() – класс для работы с FTP
ftp.Timeout = время в миллисекундах – таймаута пакета
ftp.Connect(хост, логин, пароль, порт) – коннект к FTP
ftp.SetDirectory(директория) – путь к директории на FTP (относительно корневой)
ftp.List(маска файла, ссылка на поток по значению) – вернёт список файлов, соответствующих маске, на ФТП в поток
ftp.Binary() – бинарный режим передачи данных
ftp.Retrieve(файл, .GlobalStream) – загрузка фала в поток
ftp.Delete(файл) – удаление файла
ftp.Append(имяфайла, поток) - дописать данные из потока в файл с именем
ftp.Logout() – закрытие FTP коннекта
ClassMethod FTPGetFiles(ftp, fileName) As %Status
{
s ftp = ##class(%Net.FtpSession).%New()
s ftp.Timeout = 2000
s host = "11.111.11.111"
s port = 2021
s user = "myth/user"
s pass = "userspass"
if ftp.Connect(host, user, pass, port)
{
d ftp.SetDirectory("/TestDir")
// Маска файла
s fileName = "????????t??????vK*.xml"
// Работа с файлами по маске в указанной папке
d ..ParserDir(ftp, fileName)
s fileName = "????????a??????yK*.xml"
d ..ParserDir(ftp, fileName)
}
q ftp.Logout()
}
/// Загрузка в поток файлов, удовлетворяющих маске
ClassMethod ParserDir(ftp, fileName) As %Status
{
s file = ""
s x = 1
s file(x) = ""
// Список файлов из папки в поток
d ftp.List(fileName, .stream)
q:'$IsObject(stream)
while 'stream.AtEnd
{
// Чтение по одному символу из потока
s file = stream.ReadLine(1, .sc, .eol)
if ( file = $C(10) )
{
// Формирование массива дескрипторов файла
s x = x + 1
s file(x) = ""
}
else
{
// Формирование дескриптора файла
s file(x) = file(x) _ file
}
if $$$ISERR(sc)
{
w "ERROR"
q
}
}
// Переключение в бинарный режим
d ftp.Binary()
// Получение дескриптора следующего файла
s key = $ORDER(file(""),1)
while ( key '= "" )
{
// Имя файла
s fName = $E(file(key), 40, *)
if ( $L(fName) > 0 )
{
#dim GlobalStream As %GlobalBinaryStream;
// Дописываем данные из потока в файл с именем fName. В нашем случае из-за настроек виндового FTP и писем в CP1251 необходима трансформация кодировки
d ftp.Retrieve($zcvt($zcvt(fName,"I","CP1251"),"O","CP1251"),.GlobalStream)
#dim status As %String;
// Поиск вхождения буквы в имени файла
if ( $F(fName,"s") > 0 )
{
s status = ..Parser(GlobalStream, "Product", "Shipment", parser)
}
elseif ( $F(fName,"d") > 0 )
{
s status = ..Parser(GlobalStream, "Product", "Disposal" ,parser)
}
if ( status )
{
d ftp.Delete(fName)
}
}
s key = $order(file(key),1)
}
q ftp.Logout()
}
/// Экспорт файлов на FTP сервер
ClassMethod RunExport() As %Status
{
s ftp = ##class(%Net.FtpSession).%New()
s ftp.Timeout = 2000
s host = "11.111.11.111"
s port = 2021
s user = "myth/user"
s pass = "userspass"
if ftp.Connect(host, user, pass, port)
{
d ftp.Binary()
// Получение файлов по маске в папке в порядке сортировки
s st = ##class(%SQL.Statement).%New()
d st.%PrepareClassQuery("%File","FileSet")
s rs = st.%Execute("/usr/cachesys201221/csp/sm/export_xml","*.xml","Size,Name")
while rs.%Next()
{
// Удаляем файл с сервера, если такой уже есть
d ftp.Delete(rs.%GetData(6))
// Загружаем файл в поток
s stream = ##class(%FileBinaryStream).%New()
// 1 - Полный путь до файла на нашем локальном сервере
s stream.Filename = rs.%GetData(1)
// Если файл УСПЕШНО дописан на сервер - перемещаем его в директорию "done"
if ( ftp.Append(rs.%GetData(6),stream) = $$$OK )
{
d ##class(%File).Rename(rs.%GetData(1), ##class(%File).GetDirectory(rs.%GetData(1)) _ "done/" _ rs.%GetData(6))
}
k stream
}
}
d ftp.Logout()
k ftp
q $$$OK
}
Выгрузка данных в формат xlsx.
Ничто так не радует, как горе у соседа.
Хорошо помню, как утром, по приходу в офис, мне улыбался коллега и не без доли злорадствия рапортовал о выходе новой версии Mozilla Firefox. А все потому, что задача экспорта данных в excel в наших программных продуктах была разрешена с помощью браузерного плагина, который использовал написанную на visual-C библиотеку. После смены политики безопасности приложений FireFox, мне предстояло при каждой смене версии, в лучшем случае, скачивать новую SDK и пересобирать DLL-ку (и плагин в целом), в худшем – переписывать сишный код для DLL-ки и JS парсер страницы). Про необходимость обновления плагина у пользователей и вспоминать не хочется.
Однако третьего дня и на нашей улице перевернулся грузовик с печеньем в виде простого способа экспорта в формат Excel, коим хочу поделиться с Вами:
- Берём (формируем) html таблицу с данными
- Сохраняем таблицу в текстовый файл, меняем расширение на xls
- Открываем файл, не забываем жмакнуть «да» на табличке с предупреждением и … вуаля! Эксель прекрасно распознал нашу таблицу. При необходимости ячейкам можно передавать css стили и указывать формат данных.
#server(TestProject.MakeExcelFile($("#table_to_excel").html(), "PriceList"))#
ClassMethod MakeExcelFile(Data As %String, FileName As %String) As %Status
{
s FileName = "/tkf/reports/" _ FileName _ "_Excel.xls"
s stream = ##class(%Library.FileCharacterStream).%New()
s FP = $$GetFilename^%apiCSP(FileName)
if ( $L(Data) = 22 )
{
d stream.CopyFrom(Data)
}
else
{
d stream.Write(Data)
}
d stream.SetAttribute("Content-Length",stream.Size)
d stream.SetAttribute("ContentType","application/excel")
d stream.SetAttribute("Charset","UTF8")
d stream.SetAttribute("ContentDisposition","attachment; filename=" _ $p(FileName,"/",4))
d stream.LinkToFile(FP)
d stream.SaveStream()
s oid = stream.%Oid()
&js<window.location="#url(%25CSP.StreamServer.cls?STREAMOID=#(..Encrypt(oid))#)#";>
q $$$OK
}
Ячейке, к примеру, можно задать размер шрифта — <td style=”font-size:18px”>Анна Антонова</td>
, а можно указать числовой тип формата:
<style>.toText{ mso-number-format:"@"; } </style>
<td class=”toText”>Анна Антонова</td>
Полезные советы
Сокращения
Наверняка не все знают, что в COS существуют сокращенные версии операторов, вроде этих:
Set = s
Do = d
Write = w
Kill = k
Quit = q
…
Сокращения в большинстве случаев отражены в документации примерно так:
Типизация
COS — язык без строгой типизации, но иногда полезно или даже необходимо заранее провести типизацию, для этого и была создана конструкция #dim
Пример:
$CLASSNAME в SQL
Редкий, но вполне реальный случай – при построении запроса необходимо вывести имя класса (Аналог $CLASSNAME в SQL), для этих целей можно использовать скрытое поле, которое присуще всем классам — x__classname.
Пример:
Есть 2 класса A и B, которые наследуются от общего предка Letters Extends %Persistent. Включим в выборку поле x__classname.
Версия Cache — Cache for UNIX (Red Hat Enterprise Linux for x86-64) 2012.2.1 (Build 705U) Wed Oct 24 2012 14:32:01 EDT.
Автор: Argon