- PVSM.RU - https://www.pvsm.ru -

Работа с 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

Сокращения в большинстве случаев отражены в документации примерно так:
Работа с FTP и выгрузка данных в xlsx (Caché Object Script)

Типизация

COS — язык без строгой типизации, но иногда полезно или даже необходимо заранее провести типизацию, для этого и была создана конструкция #dim

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

$CLASSNAME в SQL

Редкий, но вполне реальный случай – при построении запроса необходимо вывести имя класса (Аналог $CLASSNAME в SQL), для этих целей можно использовать скрытое поле, которое присуще всем классам — x__classname.

Пример:
Есть 2 класса A и B, которые наследуются от общего предка Letters Extends %Persistent. Включим в выборку поле x__classname.
Работа с FTP и выгрузка данных в xlsx (Caché Object Script)

Версия 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

Источник [1]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/programmirovanie/32819

Ссылки в тексте:

[1] Источник: http://habrahabr.ru/post/177609/