是否可以使用imagemin-cli并保持压缩文件的相同文件夹结构?

时间:2017-03-01 08:40:49

标签: npm-scripts imagemin

我正在尝试使用npm脚本创建imagemin脚本并使用imagemin-cli。首先,我将文件复制到dist(或.tmp for development)文件夹,然后使用此脚本压缩图像:

的package.json

...
scripts {
  "copy:dev": "cpx app/src/**/*.{html,png,jpg,mp4,webm} .tmp/",
  "copy:prod": "cpx app/src/**/*.{html,png,jpg,mp4,webm} dist/",
  "imagemin:dev": "imagemin app/src/images/**/* -o .tmp/images/",
  "imagemin:prod": "imagemin  app/src/images/**/* -o dist/images/",
  ...
},

因此,当我运行这些脚本时,压缩后所有图像都放在文件夹images/内。

有没有办法压缩图像并保留文件夹结构?也许还有其他插件或其他东西。

4 个答案:

答案 0 :(得分:5)

  

是否可以通过保留文件夹结构来压缩图像?

答案简短,不是Seq[Book] => Seq[Bookapp]

imagemin(建立在API imagemin-cli上),不提供保留文件夹结构的机制。在项目github repo中查看 open issue / feature-request #191

解决方案

实现您的要求的跨平台方法是编写直接使用imagemin API的自定义 node.js 实用程序脚本。如此有效......构建您自己的CLI工具,可以通过imagemin-cli运行。

以下要点展示了如何实现这一目标......

<强> imagemin.js

实用程序 node 脚本如下:

npm-scripts

注意: 上面的代码使用#!/usr/bin/env node 'use strict'; var path = require('path'); var readline = require('readline'); var Imagemin = require('imagemin'); var outdir = process.env.PWD; // Default output folder. var verbose = false; // Default no logging. // The folder name specified MUST exist in the `glob` pattern of the npm-script. var DEST_SUBROOT_FOLDER = 'images'; // Nice ticks for logging aren't supported via cmd.exe var ticksymbol = process.env.npm_config_shell.indexOf('bash') !== -1 ? '✔' : '√'; var rl = readline.createInterface({ input: process.stdin, output: null, terminal: false }); // Handle the optional `-o` argument for the destination folder. if (process.argv.indexOf('-o') !== -1) { outdir = process.argv[process.argv.indexOf('-o') + 1]; } // Handle the optional `-v` argument for verbose logging. if (process.argv.indexOf('-v') !== -1) { verbose = true; } /** * Utilizes the Imagemin API to create a new instance for optimizing each image. * @param {String} srcpath - The filepath of the source image to optimize. * @param {String} destpath - The destination path to save the resultant file. * @param {Function} - The relevent `use` plugin (jpegtran|optipng|gifsicle). */ function imagemin(srcpath, destpath, plugin) { var im = new Imagemin() .src(srcpath) .dest(destpath) .use(plugin); im.optimize(function (err, file) { if (err) { console.error('Error: ' + err); process.exit(1); } if (file && verbose) { console.log('\x1b[32m%s\x1b[0m', ticksymbol, destpath); } }); } /** * Obtains the destination path and file suffix from the original source path. * @param {String} srcpath - The filepath for the image to optimize. * @return {{dest: String, type: String}} dest path and ext (.jpg|.png|.gif). */ function getPathInfo(srcpath) { var ext = path.extname(srcpath), parts = srcpath.split(path.sep), subpath = parts.slice(parts.indexOf(DEST_SUBROOT_FOLDER), parts.length); subpath.unshift(outdir); return { dest: path.normalize(subpath.join(path.sep)), ext: ext }; } /** * Triggers the relevent imagemin process according to file suffix (jpg|png|gif). * @param {String} srcpath - The filepath of the image to optimize. */ function optimizeImage(srcpath) { var p = getPathInfo(srcpath); switch (p.ext) { case '.jpg': imagemin(srcpath, p.dest, Imagemin.jpegtran({ progressive: true })); break; case '.png': imagemin(srcpath, p.dest, Imagemin.optipng({ optimizationLevel: 5 })); break; case '.gif': imagemin(srcpath, p.dest, Imagemin.gifsicle({ interlaced: true })); break; } } // Read each line from process.stdin (i.e. the filepath) rl.on('line', function(srcpath) { optimizeImage(srcpath); }); API的版本1.0.5,而不是最新版本 - 为什么?请参阅下面“附加说明”部分下的第1点。)

卸载并安装新软件包

  1. 首先卸载imagemin,因为它不再需要:
  2. imagemin-cli

    1. 下一步安装imagemin版本 $ npm un -D imagemin-cli (这是一个较旧的软件包,因此安装时间可能会比{c}长{}}
    2. 1.0.5

      1. 然后安装cli-glob。这将用于指定glob图案以匹配图像以进行优化。
      2. npm

        <强> NPM-脚本

        按如下方式更新您的$ npm i -D imagemin@1.0.5

        $ npm i -D cli-glob

        注意: 要使用上面显示的要点优化图片,不必使用名为npm-scripts和{的两个脚本您原来的帖子/问题中显示的{1}}

        1. 上述脚本的... "scripts": { "imagemin:prod": "glob \"app/src/images/**/*.{png,jpg,gif}\" | node bin/imagemin -v -o dist", "imagemin:dev": "glob \"app/src/images/**/*.{png,jpg,gif}\" | node bin/imagemin -v -o .tmp", ... }, ... 部分使用cli-glob来匹配必要的图像源文件。

        2. 然后将路径传送到copy:prod实用程序节点脚本。

        3. 当包含copy:dev(详细)参数/标志时,每个处理过的图像都会记录到控制台。要省略日志记录,只需删除glob \"app/src/...标记。

        4. imagemin.js(输出)参数/标志用于指定目标文件夹名称。例如。 -v-v。省略-o的值时,结果图像将输出到项目根目录。

        5. 附加说明:

          1. 使用dist版本1.0.5的原因是因为此API 允许将.tmp值指定为单个文件路径。在大于-o的版本中,API期望imagemin值为glob模式,如最新版本5.2.2中所示。

          2. 上述要点假设src保存在名为2.0.0的文件夹中,该文件夹与src位于同一文件夹中。它可以通过在前面添加点[。]来更改为首选名称或不可见文件夹,例如imagemin.jsbin。无论您选择什么,您都需要相应地更新package.json中脚本的路径。

答案 1 :(得分:2)

更新2020

由GijsRogé合并(截至2020年中旬)pull request,可以在输出目录中保留目录结构。

您可以通过直接从Github安装,引用仓库甚至特定的提交来安装注册表中未列出的npm模块:
npm install https://github.com/<username>/<repository>#<commit> --save-dev

要使用GijsRogé的修复程序安装imagemin,请运行...
npm install https://github.com/imagemin/imagemin#bfd7c547045f68ed92243c6a772f6265a08a687f --save-dev

...并通过设置preserveDirectories: true在脚本中启用新选项:

// Note: imports and plugin configs have been omitted for brevity

const imagemin = require('imagemin');
const imageminMozjpeg = require('imagemin-mozjpeg');
...

(async () => {
    const files = await imagemin(['input_dir/**/*.{jpg,jpeg,png,svg}'], {
    destination: 'output_dir/',
    ✨preserveDirectories: true,
        plugins: [
            imageminMozjpeg( ... ),
            imageminPngquant( ... ),
            imageminSvgo( ... )
        ]
});

.jpg中找到的input_dir/some/sub/dir/image.jpg现在将被处理并写入output_dir/input_dir/some/sub/dir/image.jpg

使用destination: '.'覆盖原始文件。

答案 2 :(得分:0)

我也遇到了同样的问题,但是我更改了节点模块中imagemin的index.js文件。请将代码复制粘贴到节点模块中

'use strict';
const fs = require('fs');
const path = require('path');
const fileType = require('file-type');
const globby = require('globby');
const makeDir = require('make-dir');
const pify = require('pify');
const pPipe = require('p-pipe');
const replaceExt = require('replace-ext');

const fsP = pify(fs);

const handleFile = (input, output, options) => fsP.readFile(input).then(data => {
	const dest = output ? output : null;

	if (options.plugins && !Array.isArray(options.plugins)) {
		throw new TypeError('The `plugins` option should be an `Array`');
	}

	const pipe = options.plugins.length > 0 ? pPipe(options.plugins)(data) : Promise.resolve(data);

	return pipe
		.then(buffer => {
			const ret = {
				data: buffer,
				path: (fileType(buffer) && fileType(buffer).ext === 'webp') ? replaceExt(dest, '.webp') : dest
			};

			if (!dest) {
				return ret;
			}

			return fsP.writeFile(ret.path, ret.data)
				.then(() => ret)
				.then(function(result) {})
		})
		.catch(error => {
			error.message = `Error in file: ${input}\n\n${error.message}`;
			throw error;
		});
});

module.exports = (input, output, options) => {
	if (!Array.isArray(input)) {
		return Promise.reject(new TypeError(`Expected an \`Array\`, got \`${typeof input}\``));
	}

	if (typeof output === 'object') {
		options = output;
		output = null;
	}

	options = Object.assign({plugins: []}, options);
	options.plugins = options.use || options.plugins;

	return globby(input, {onlyFiles: true}).then(paths => Promise.all(paths.map(x => handleFile(x, output, options))));
};

module.exports.buffer = (input, options) => {
	if (!Buffer.isBuffer(input)) {
		return Promise.reject(new TypeError(`Expected a \`Buffer\`, got \`${typeof input}\``));
	}

	options = Object.assign({plugins: []}, options);
	options.plugins = options.use || options.plugins;

	if (options.plugins.length === 0) {
		return Promise.resolve(input);
	}

	return pPipe(options.plugins)(input);
};

答案 3 :(得分:0)

以下脚本为每个文件夹运行单独的 Auth::user(); 作业。

它解决了同样的问题。

imagemin