Работа с FTP и выгрузка данных в xlsx (Caché Object Script)

от автора

Предлагаю Вашему вниманию статью на следующие на темы:

  1. Работа с FTP сервером с помощью %Net.FtpSession
  2. Простой способ выгрузки данных в формат xls
  3. Несколько полезных советов

Работа с 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 коннекта 
Пример импорта файлов с 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 сервер

/// Экспорт файлов на 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.

ссылка на оригинал статьи http://habrahabr.ru/post/177609/


Комментарии

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

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