Работа с ZIP-архивами из-под lisp’a
Понадобилось мне тут обрабатывать zip-архивы. Казалось бы, все должно быть просто, поскольку обработка zip-архивов уже в Windows встроена. Ан нет, ни фига.
Недолгий поиск привел на темы http://www.theswamp.org/index.php?topic=37416.0 и http://www.theswamp.org/index.php?topic=31097.msg366864#msg366864. И я радостно стал использовать представленные там коды.
Правда, счастье было неполным и очень недолгим. Причин оказалось несколько: как выяснилось, только операции с одиночными файлами (заархивировать, распаковать и т.п.) выполняются на ура. Как только надо обработать что-то пакетно (запаковать, например, каталог со всеми подкаталогами; или распаковать) - начинаются чудеса. И заключаются они в том, что при пакетной обработке какие-то файлы могут запросто не заархивироваться. Или не распаковаться.
Поскольку у меня не было никакого желания разбираться с глубинными причинами подобного поведения, я нашел обходные пути.
Так, для распаковки можно сделать vbs-файл и заставить его выполняться:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | (defun _kpblc-zip-extract-allfiles (zipfile strdest / file handle wscript) ;| * Извлечение всех файлов из zip-архива (включая подкаталоги). Источник: * http://www.theswamp.org/index.php?topic=31097.0 * Параметры вызова: zipfile Полное имя zip-архива strdest Путь, куда выполнять разархивирование. * Пример вызова: (_kpblc-zip-extract-allfiles "C:\\pack.zip" "C:\\updates") |; (setq file (strcat (vl-string-right-trim "\" (getenv "temp")) "\\unzip.vbs") handle (open file "w") ) ;_ end of setq (foreach item (list (strcat "ZipFile="" zipfile """) (strcat "ExtractTo="" strdest """) "'If the extraction location does not exist create it." "Set fso = CreateObject("Scripting.FileSystemObject")" "If NOT fso.FolderExists(ExtractTo) Then" " fso.CreateFolder(ExtractTo)" "End If" "'Extract the contants of the zip file." "set objShell = CreateObject("Shell.Application")" "set FilesInZip=objShell.NameSpace(ZipFile).items" "objShell.NameSpace(ExtractTo).CopyHere(FilesInZip)" "Set fso = Nothing" "Set objShell = Nothing" ) ;_ end of list (write-line item handle) ) ;_ end of foreach (close handle) (if (setq wscript (vlax-get-or-create-object "WScript.Shell")) (vl-catch-all-apply (function (lambda () (vlax-invoke-method wscript "Run" file 0 :vlax-true)))) ) ;_ end of if (vl-catch-all-apply (function (lambda () (vlax-release-object wscript)))) (vl-file-delete file) ) ;_ end of defun |
Честно скажу, код VBS не мой, спер откуда-то.
Чем хорош подобный код - так это тем, что каталог вроде бы даже и создавать предварительно не надо, эту работу выполнит VBS. Чем плох - так это тем, что админы "с добрыми усталыми глазами и печальной улыбкой на лице" могут заблокировать выполнение VBS-кода. Ну, если заблокируют, буду придумывать что-нибудь еще.
С архивированиеи я решил задачу по-другому. Попытался в очередной раз использовать C#:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | using System; using System.IO; using System.IO.Compression; using System.Security.Cryptography; using AcRt = Autodesk.AutoCAD.Runtime; using AcDb = Autodesk.AutoCAD.DatabaseServices; namespace kpblc { [AcRt.LispFunction("_kpblc-zipfolder")] public static bool kpblc_zipfolder(AcDb.ResultBuffer arguments) { bool res = false ; if (arguments != null) { Array args = arguments.AsArray(); string zipDest = System.Convert.ToString(((AcDb.TypedValue)(args.GetValue(0))).Value); string folderName = System.Convert.ToString(((AcDb.TypedValue)(args.GetValue(1))).Value); try { ZipFile.CreateFromDirectory(folderName, zipDest, CompressionLevel.Optimal, false); res = true; } catch { res = false; } } return res; } } } |
Сборка всяко загружается внутрь ACAD'a, так что можно спокойно использовать _kpblc-zipfolder и не париться.