[Урок 17] . Копирование, удаление и переименование папок и файлов с помощью iMacros+JS

Вот мы  и добрались до еще одной важной темы. Сегодня мы очень глубоко окунемся в механизмы работы с файлами и папками в iMacros. Когда я задумал разбирать эту тему, первое, что пришло в голову, это использование возможности запускать любое приложение из iMacros, как из командной строки. То есть использовать стандартные виндовые или иксовые команды, типа copy, mkdir и т.д. Такой вариант не супер-клевый, и слишком примитивный, поэтому он нам не подходит. Для работы с файлами я решил использовать один из встроенных модулей Firefox — FileUtils. В итоге, на свет, моими стараниями, получилась небольшая, но очень удобная библиотека для работы с файлами и папками под iMacros.

Что за зверь FileUtils.jsm?

Скажу сразу, что мы будем для выполнения своих задач использовать модуль FileUtils.jsm и делать это будем в Firefox 35 и iMacros 8.9.7. Этот модуль обеспечивает работу с файлами и папками, если вы когда-нибудь писали расширения под Firefox, то наверняка с ним сталкивались.

Подключим его мы следующим образом:

Components.utils.import("resource://gre/modules/FileUtils.jsm");
window.console.log(FileUtils);

Это не единственный вариант подключения, но я остановлюсь на нем. После чего, нам будет доступен новый объект FileUtils, я обычно сразу подобные новые объекты распечатываю в консоли и смотрю, какие свойства и методы он имеет.

Хм, не густо и везде классические обертки с интерфейсами подключения. Однако, я четко вижу интересный объект под названием File, который по умолчанию undefined.
Немного гугла спустя, я наткнулся на документацию по этому объекту https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIFile
Не обращайте внимания на небольшое несовпадения названий объекта. Бегло пробежавшись по документации, я понял, что там есть все необходимое.

Основная концепция тут, как в Линуксе — «Всё — есть файл.»  

Распишу это немного подробнее. Представим, что у нас есть файл c://test.txt. Мы создаем новый объект типа File и в качестве параметра передаем ему путь. Что-то у меня не очень получилось в консоли посмотреть все свойства и объекты свежесозданного объекта, однако я использовал цикл for .. in для вывода всех ключей и значения объекта File.

Components.utils.import("resource://gre/modules/FileUtils.jsm");
var file = new FileUtils.File("C:\\test.txt");
window.console.log(file);

for (var item in file) {
	window.console.log(item, ' : ', file[item]);
}

Получилось примерно так и вылезли мелкие ошибки при выводе, что не страшно:

Судя по методам isFile() и isDirectory(), этот модуль предоставляет одинаковый подход при работе, как с файлами, так и с папками. От этого и будем плясать.

Разбираем пример удаления файла

Почему-то моментально вспоминается встроенная в iMacros команда FILEDELETE NAME=c:\test.txt. Почему-то разработчики сразу не озаботились и остальными файловыми операциями. Исправим эту несправедливость!

Для удаления файла, нам сначала необходимо проверить, а существует ли он по указанному пути. К счастью, для этого есть встроенный метод exists(). А для удаления файла есть встроенный метод remove(). Выглядит все очень просто и здорово, поскольку все операции по взаимодействию с операционной системой и файловой системой, этот модуль берет на себя.

Вот пример для удаления файла:

var file = new FileUtils.File("C:\\test.txt");

if (file.exists()) {
	try {
		file.remove(true); // если true - то удаление рекурсивно(подходит для папок)		
	} catch (e) {
		window.console.log(e);
	}
}

В первой строке мы создаем и получаем дескриптор файла. Грубо говоря, это начальная настройка. На том этапе еще не известно, будем ли мы создавать файл с таким именем или удалять папку по этому пути. Получается очень универсально. Далее мы проверяем существует ли такой файл или папка, после чего удаляем, чтобы это ни было. Метод remove() можно запускать без параметров или с параметром. Если указать true в качестве параметра — то если это папка, то удалится все ее содержимое и она сама.

Это основная концепция, которую нужно понимать. Все остальные операции — это создание объекта с папкой или файлом и наборы методов, подобных remove(). Расписывать каждый — не вижу смысла, поэтому приведу код своей библиотеки, которая у меня в итоге получилась.

Библиотека для работы с файлами и папками в iMacros

Есть в нашей среде iMacros-ботоводов люди, которые боятся делиться своими наработками. Я за свободную информацию и считаю, что это здорово выкладывать свои эксперименты и куски кода, даже если на их создание ушли недели и они супер-уникальные и крутые. Нельзя сидеть на мешке со своим опытом и держаться за него. Всегда надо идти вперед и придумывать что-то еще более крутое до бесконечности. Так что, ловите мой небольшой вклад в идеи всемирной роботизации;)

Ладно, меньше лирики, больше кода. Я не стал включать сюда методы по чтению и записи файлов, но вы можете добавить их самостоятельно из урока 9.

Код библиотеки:

/**
 * File/Folders Manager(FFM) for iMacros 8.9.7
 *
 * @version: 1.0
 * @author:  Nagibaka <https://nagibaka.ru>
 * 
 */

// Подключаем встроенную библиотеку Firefox,
// после чего нам будет доступен объект FileUtils
Components.utils.import("resource://gre/modules/FileUtils.jsm");


/***************************************************************************
 *   Л И Б А   П О   Р А Б О Т Е   С   Ф А Й Л А М И   И   П А П К А М И   *
 ***************************************************************************/
var FFM = {


	/**
	 * Функция удаления файла или папки со всем содержимым
	 * @param  {String} path Путь к файлу\папке
	 * @return {Boolean}      
	 */
	removeFile: function (path) {
		var file = new FileUtils.File(path);

		if (file.exists()) {
			try {
				file.remove(true); // если true - то удаление рекурсивно(подходит для папок)
				return true;
			} catch (e) {
				window.console.log(e);
				return false;
			}
		} else {
			return false;
		}
	},


	/**
	 * Функция переименования файла
	 * @param  {String} oldFilePath Полный путь к файлу
	 * @param  {String} newFileName Новое имя для файла
	 * @return {Boolean}
	 */
	renameFile: function (oldFilePath, newFileName) {	
		// Извлекаем путь до папки с файлом
		var path = (/(\w?\:?\\?[\w\-_\\]*\\+)([\w-_]+\.[\w-_]+)/gi).exec(oldFilePath)[1];

		var file = new FileUtils.File(oldFilePath); // Тут инстанс файла
		var oldDir = new FileUtils.File(path);      // Тут инстанс папки

		if (file.exists()) {
			try {
				file.renameTo(oldDir, newFileName); 
				return true;
			} catch (e) {
				window.console.log(e);
				return false;
			}
		} else {
			return false;
		}
	},


	/**
	 * Функция проверяет существует ли файл или папка
	 * @param  {String}  path Полный путь к файлу или папке
	 * @return {Boolean}
	 */
	isExists: function (path) {
		return new FileUtils.File(path).exists();
	},


	/**
	 * Функция проверяет файл это или нет
	 * @param  {String}  path Полный путь к файлу
	 * @return {Boolean}
	 */
	isFile: function (path) {
		return new FileUtils.File(path).isFile();
	},


	/**
	 * Функция проверяет папка это или нет
	 * @param  {String}  path Полный путь к папке
	 * @return {Boolean}
	 */
	isFolder: function (path) {
		return new FileUtils.File(path).isDirectory();
	},


	/**
	 * Функция для создания новой папки
	 * @param  {String} path Имя новой папки(полный путь)
	 * @return {Boolean}
	 */
	mkDir: function (path) {
		var file = new FileUtils.File(path);
		try {
			file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0777);
			return true;
		} catch (e) {
			window.console.log(e);
			// если такая директория уже есть, правильнее будет возвращать положительный(true) результат
			return e.name === "NS_ERROR_FILE_ALREADY_EXISTS" ? true : false; 
		}
	},


	/**
	 * Функция удаления папки(удаляет все содержимое тоже)
	 * @param  {String} path Полный путь до папки
	 * @return {Boolean}
	 */
	rmDir: function (path) {
		// АККУРАТНО! Удаляет все вложенные папки и файлы
		return this.removeFile(path);
	},


	/**
	 * Функция для копирования одного файла
	 * @param  {String} from Полный путь до файла, который копируем
	 * @param  {String} to   Полный путь к файлу, куда копируем
	 * @return {Boolean}     true - если скопировалось
	 */
	copyFile: function (from, to) {			
		var toFileName =   (/(\w?\:?\\?[\w\-_\\]*\\+)([\w-_]+\.[\w-_]+)/gi).exec(to)[2];
		var toFolderPath =   (/(\w?\:?\\?[\w\-_\\]*\\+)([\w-_]+\.[\w-_]+)/gi).exec(to)[1];

		var file   = new FileUtils.File(from);                // Тут инстанс файла, который копируем
		var newFolderPath = new FileUtils.File(toFolderPath); // Тут инстанс папки, куда копируем		

		if (file.exists()) {
			try {
				// АККУРАТНО! Если файл в папке уже есть - он перезаписывается автоматом.
				file.copyTo(newFolderPath, toFileName); 
				return true;
			} catch (e) {
				window.console.log(e);
				return false;
			}
		} else {
			return false;
		}
	},


	/**
	 * Функция для перемещения одного файла
	 * @param  {String} from Полный путь до файла, который копируем
	 * @param  {String} to   Полный путь к файлу, куда копируем
	 * @return {Boolean}     true - если скопировалось
	 */
	moveFile: function (from, to) {			
		var toFileName   =   (/(\w?\:?\\?[\w\-_\\]*\\+)([\w-_]+\.[\w-_]+)/gi).exec(to)[2];
		var toFolderPath =   (/(\w?\:?\\?[\w\-_\\]*\\+)([\w-_]+\.[\w-_]+)/gi).exec(to)[1];

		var file   = new FileUtils.File(from);                // Тут инстанс файла, который копируем
		var newFolderPath = new FileUtils.File(toFolderPath); // Тут инстанс папки, куда копируем		

		if (file.exists()) {
			try {
				file.moveTo(newFolderPath, toFileName); 
				return true;
			} catch (e) {
				window.console.log(e);
				return false;
			}
		} else {
			return false;
		}
	},


	/**
	 * Функция для копирования папки со всеми вложенными файлами и папками
	 * @param  {String} from Полный путь к папке, которую копируем
	 * @param  {String} to   Полный путь к папке, куда копируем
	 * @return {Boolean}
	 */
	copyDir: function (from, to) {	
		var folderName   = (/(\w?\:?\\?[\w\-_\\]*\\+)([\w-_]+)/gi).exec(from);
		var fromFolder   = new FileUtils.File(from);  // Тут инстанс папки, который копируем
		var toFolder     = new FileUtils.File(to);    // Тут инстанс папки, куда копируем		

		if (fromFolder.exists()) {
			try {
				fromFolder.copyTo(toFolder, folderName); 
				return true;
			} catch (e) {
				window.console.log(e);
				return false;
			}
		} else {
			return false;
		}
	},


	/**
	 * Функция возвращает массив со списком файлов и папок по указанному пути
	 * @param  {String} path Полный путь к папке
	 * @return {Array}      Массив объектов с подробной информацией о каждом
	 */
	listFiles: function (path) {
		var folder = new FileUtils.File(path);
		var entries = folder.directoryEntries;
		var list = [];

		while(entries.hasMoreElements()) {
			var entry = entries.getNext();
			entry.QueryInterface(Components.interfaces.nsIFile);
			list.push({
				fullPath: entry.path,
				name: entry.leafName,
				size: entry.fileSize,
				isFile: entry.isFile(),
				isDirectory: entry.isDirectory(),
				isReadable: entry.isReadable(),
				isWritable: entry.isWritable(),
				isHidden: entry.isHidden()
			});
		}

		return list;
	},


	/**
	 * Функция для получения доступных нам локальных дисков
	 * @return {Array} Возвращает массив со списком локальных дисков
	 */
	getDrives: function () {
		var root = new FileUtils.File("\\\\.");

		var drivesIterator = root.directoryEntries;
		var drives = [];

		while (drivesIterator.hasMoreElements()) {
			var info = drivesIterator.getNext().QueryInterface(Components.interfaces.nsILocalFile);
			drives.push({
				path: info.path,
				freeSpaceInMb: Number(info.diskSpaceAvailable / 1024 / 1024).toFixed(0)
			});
		}
		
		return drives;
	}

}

Документация и примеры использования

Для работы примеров, приведенных ниже, вам необходимо сначала один раз вставить весь код библиотеки в ваш макрос и ниже пробовать примеры.
Для компактности я буду вместо if-else использовать тернарные выражения.

Например:

// IF-ELSE пример
if (FFM.isExists("C:\\test.txt")) {
	alert('Файл существует!');
} else {
	alert('Файл не существует!');
}

// Тернарные выражения(Абсолютно аналогично)
alert(FFM.isExists("C:\\test.txt") ? 'Файл существует!' : 'Файл не существует!');

FFM.getDrives()

Получает массив объектов, содержащих информацию о букве диска и количестве свободного места на нем.

window.console.log(FFM.getDrives());
/* 
	Результат(если у вас один диск c://):
	[
		{
			freeSpaceInMb: "170791",
			path: "C:"
		}
	]

 */

FFM.listFiles(«путь к папке»)

Эта функция возвращает массив объектов. Каждый объект содержит информацию о файле или папке. Можно использовать для создания своего кастомного файл-менеджера.

window.console.log(FFM.listFiles("c:\\BOTS\\"));
/*
	Результат - массив с объектами, типо такого
	[
		{
			fullPath : "c:\BOTS\ff35.zip",
			isDirectory : false, // это папка?
			isFile : true,       // это файл?
			isHidden : false,    // скрытый объект?
			isReadable : true,   // доступен для чтения?
			isWritable : true,   // доступен для записи?
			name : "ff35.zip",   // имя файла\папки
			size : 82519946      // размер в байтах, если это папка, то указывает 4096 или другую некорректную цифру
		}
	]
*/

FFM.mkDir(«путь создаваемой папки»)

Эта функция создает папки и подпапки, если они не существуют. Возвращает true, если успешно, иначе false.

//Создадим три вложенных папки
FFM.mkDir("c:\\folder1\\folder2\\folder3\\");

FFM.rmDir(«путь создаваемой папки»)

Эта функция удаляет папки со всем содержимым. Возвращает true, если успешно, иначе false.

FFM.rmDir("c:\\folder1\\");

FFM.copyDir(«путь к папке, которую копируем», «путь к папке, в которую копируем»)

Эта функция полностью копирует папку и ВСЕ ее содержимое. Возвращает true, если успешно, иначе false.

FFM.copyDir("c:\\folder1\\", "c:\\folder2\\");
// В папке folder2 появится вложенная папка folder1 со всем содержимым

FFM.isExists(«путь к файлу»)

Эту функция определяет, существует ли папка или файл. Возвращает true, если есть, и false, если нету.

alert(FFM.isExists("C:\\test.txt") ? 'Файл существует!' : 'Файл не существует!');

FFM.isFile(«путь к файлу»)

Эту функция определяет, существует ли папка или файл. Возвращает true, если есть, и false, если нету.

alert(FFM.isFile("C:\\test.txt") ? 'Это файл!' : 'Это не файл(наверно папка)!');

FFM.isFolder(«путь к файлу»)

Эту функция определяет, существует ли папка или файл. Возвращает true, если есть, и false, если нету.

alert(FFM.isFolder("C:\\") ? 'Это папка!' : 'Это не папка(наверно файл)!');

FFM.renameFile(«путь к файлу», «имя нового файла»)

Эта функция переименовывает файл. Первый параметр — это полный путь к файлу. Второй параметр — это новое имя файла. Если переименование было успешным — возвращает true, иначе — false.

FFM.renameFile("c:\\test.txt", "renamed.txt");

FFM.removeFile(«путь к файлу»)

Эта функция удаляет файл. Первый параметр — это полный путь к файлу.  Если удаление было успешным — возвращает true, иначе — false.

FFM.removeFile("c:\\test.txt");

FFM.copyFile(«путь к исходному файлу», «путь к файлу, куда копируем»)

Эта функция копирует один файл. Первый параметр — это полный путь к файлу, который необходимо скопировать. Второй параметр — это путь к файлу, куда мы хотим копировать. Вы сразу же можете изменить имя скопированного файла.  Можно копировать в любую существующую папку. Если копирование было успешным — возвращает true, иначе — false.

FFM.copyFile("c:\\test.txt", "c:\\copy-test.txt");

FFM.moveFile(«путь к исходному файлу», «путь к файлу, куда переместить»)

Аналогично прошлой функции, только тут файл перемещается, а из начального пути удаляется. Его также можно сразу переименовать.

FFM.moveFile("c:\\test.txt", "c:\\copy-test.txt");

Заключение

Статья получилась гигантской и дико полезной. Это первая версия библиотеки и она пока сыровата. Не хватает пуленепробиваемой обработки любых ошибок. Сейчас, это базовая заготовка, зато в ней несложно разобраться и взять что-то себе на вооружение.

Пишите ваши отзывы, вопросы и пожелания в комментариях!

10 комментариев

  1. Добрый день, при попытке импорта хоть какой-то сторонней библиотеки js в файл imacros, бесконечно получаю ошибку Window/Document is not defined, единственное получилось подключить jquery, на этом все. Перерыл весь гугл, везде какие-то конкретные проблемы с какими-то либами, но не со всеми библиотеками вообще, заранее спасибо за помощь.

    Ответить
    • Попробовал browserify, то же самое, если заменить все document на window.document начинает просить что-то другое, всегда что-то undefined.

      Ответить
      • Никогда сторонние библиотеки не подключал, зато создал свою, обьявил все нужные функции глобально ( без var), и подхватываю её через евал.

        без понятия, как выглядят библиотеки, но может вам поможет)

        eval( imns.FIO.readTextFile( imns.FIO.openNode(«C:\\BOTS\\iMacros\\lib.js») ) );

        Ответить
  2. Я за 2 года paбoты в cкинебoди cтольkо нe имeла сkoлbкo здесb. Арсeний cпacибo тeбе бoльшое. Ceйчaс я тoчно бpoшу зaниматbcя ерундoй с этими таблетkами)))) A то и нa внykов вpемeни не хваталo с такой пoдрабoткой. Тепеpb подaрkoв смoгy всем нakyпитb и доkазатb чтo финaнсы в нете можнo пoлyчaтb!

    Ответить
  3. Очень годная библиотека. Может знаешь как imacros-ом можно менять cookie, для автоматизации многопользовательного бота?

    Ответить
  4. Что делать если например FRAME F= любое значение принимает при каждой перезагрузки страницы? Как поставить постоянное значение?

    Ответить
  5. Подскажите, как поступить , если известна только часть в названии файла?
    Нужно переименовывать файл, который скачивает бот.
    Вижу 2 возможных решения:
    а. Регулярка — Как реализовать?
    б. Отследить название файла при скачивании — Возможно ли это?

    Ответить
  6. Здравствуйте, вы занимаетесь разработкой скриптов imacros+javascript на заказ? Если да то сколько будет стоить проект ?

    Ответить
  7. В чём подвох? Почему так наворочено?
    // Извлекаем путь до папки с файлом
    //var str = (/(\w?\:?\\?[\w\-_\\]*\\+)([\w-_]+\.[\w-_]+)/gi).exec(«C:\\Users\\username\\Desktop\\test.txt»)[1];

    Не проще?
    var str = «C:\\Users\\username\\Desktop\\test.txt».match(/(.+)\\/)[1];
    window.console.log(str);

    Ответить

Оставить комментарий