React-Native中的图像压缩

6

我正在尝试使用mozjpeg压缩图片,当我按照文档在node.js中实现时它运行良好。

const input = fs.readFileSync("in.ppm");
const out = mozjpeg.encode(input, { quality: 85 });

我需要在客户端对文件进行压缩,所以我尝试使用React Native来执行相同的操作,因为React Native不包含核心Node模块,如fs,所以我需要使用第三方库react-native-fs来读取文件。
当我尝试在React Native中执行mozjpeg.encode(input, { quality: 85 });时,它会抛出Unrecognized input file format --- perhaps you need -targa的异常信息。
服务器端实现。
const mozjpeg = require("mozjpeg-js");
const fs = require("fs");

const input = fs.readFileSync(filePath);
const out = mozjpeg.encode(input, { quality: 85 });
console.error(out.stderr);
fs.writeFileSync("out.jpg", out.data);

客户端实现

fs.readFile(image.path).then(data => {
    const out = mozjpeg.encode(data, { quality: 85 });
    console.log(out);
}

以下是我尝试的事情列表

  • 尝试使用十六进制、缓冲区、base64和纯URL字符串作为输入。
  • 由于Android URL的前缀包含file://,因此我尝试将其删除。

2
我使用了 https://github.com/bamlab/react-native-image-resizer 来压缩图像并保持质量。 - tuledev
是的,我目前正在使用它,但我想迁移到mozjpeg来减小文件大小。 - Badri
问题几乎肯定与编码有关(假设文件被正确读取)。因为 mozjpeg 期望文件是二进制编码的,所以可能需要像 Buffer.from(data, 'utf8') 这样的东西,因为 react-native-fsreadFile 默认是 utf8 编码。 - Trobol
4个回答

2
实际上我不熟悉mozjpeg,但我更喜欢使用纯JavaScript的方法来解决我的问题。
我猜你可能想到了NodeJS的解决方案,但是在react-native环境下,建议使用React Native Compress Image或者React Native Image Resizer
基于我的经验,我更喜欢使用后者,即React Native Image Resizer
安装完成后,可按以下方式使用:
ImageResizer.createResizedImage(
  imageUri,
  newWidth,
  newHeight,
  compressFormat,
  quality
)
  .then( resizedImageUri => {

    // the resizedImageUri is accessible here to use.

  }).catch( err => {

  // Catch any error here.

});

1
我已经在使用React Native Image Resizer,我只想从那里迁移到mozjpeg。 - Badri
亲爱的@Badri,为什么你想要从很棒的awesome库和更新的库迁移到另一个环境的库呢?这个库不兼容并且存在许多问题。 - AmerllicA
1
我比较了两种压缩方式,对我来说MozJPEG似乎具有更好的压缩技术,并且与React Native Compression相比,输出文件大小降低了20%以上,这对我正在开发的项目非常重要。 - Badri
@Badri,我们有三个环境:RN、Node和Browser。在浏览器中,压缩器方法使用Canvas和Base64来计算图像并进行一些更改,例如调整大小、旋转或压缩。在Node环境中,mozjpeg 使用 cjpeg 基于MozJPEGfs,但此模块不存在于RN环境中。在RN中需要使用设备本地模块,iOS: Objective-C和Android: Java。我知道你说MozJPEG在压缩方面更好,但它与RN环境无关。 - AmerllicA
1
是的,我完全同意你的观点。RN没有fs,但有一个叫做react-native-fs的包,用于读取文件,输出将为base64/ascii或utf-8格式。由于MozJpeg仅支持二进制缓冲区,我尝试进行转换,但显然没有成功。我的问题是,在转换为二进制缓冲区的过程中是否有遗漏。顺便说一下,我已经使用了你建议的两个库:react-native-crop-picker用于选择图像,react-native-image-resizer用于调整大小到各种尺寸。我已经尽力进行了研究,需要减少10-15%的大小并获得最佳质量。 - Badri
显示剩余2条评论

2
另外,还有一个很好的React-Native图片裁剪选择器库,其目的是获取一些图片并进行裁剪,但它具有压缩图像的良好能力。也许这个库有一个像mozjpeg一样好的压缩算法。

它可以打开相机、打开相册或使用常量图像,甚至可以关闭裁剪:

ImagePicker.openCamera({
  width: 300,
  height: 400,
  compressImageQuality: 0.2
}).then(image => {
  // do what you want
}).catch(e => {
  // handle error
});

配置简单且有许多调整选项。希望它能对您有所帮助。


亲爱的@Badri,如果可能的话,请在GitHub或GitLab上发布一个简单的、可复制的项目,然后我就可以克隆它。也许我能帮到你。 - AmerllicA
谢谢,我会尽快发布链接。 - Badri
react-native-image-crop-picker 是一个很棒的包,但是压缩质量一般。你可以找到其他的包,比如 imagemin,它们可以更好地压缩质量和文件大小。但可惜这些包并不适用于 react native。 - Anh Nguyen
@AnhNguyen,抱歉,我不明白你的意思!你发表这条评论的目的是什么?我能为你做些什么? - AmerllicA
@AmerllicA 不,我不是在求你的帮助。我只是评论了你的答案。我不能吗? - Anh Nguyen
亲爱的@AnhNguyen,当然可以,我只是没有理解。好的,谢谢。 - AmerllicA

1
你可以在mozjpeg-js文档中找到,输入参数为:

一个数据的类型数组或缓冲区


fs.readFile在客户端(react-native-fs)中返回类型为Promise<string>,并返回内容。(文档)

但在服务器端(fs),fs.readFileSync返回缓冲对象。(文档)


所以你可以使用这个函数将字符串转换为类型化数组:

function str2ta(str) {
  var bufView = new Uint16Array(str.length*2); // 2 bytes for each char
  for (var i=0, strLen=str.length; i<strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }
  return bufView;
}

我在文档中看到过,所以我提到我已经使用十六进制、缓冲区、base64和纯URL字符串作为输入。顺便说一句,我使用了这个函数来转换base64ToHex(str) { console.log(str) const raw = atob(str); let result = ''; for (let i = 0; i < raw.length; i++) { const hex = raw.charCodeAt(i).toString(16).toLowerCase(); result += ((hex.length === 2 ? hex : '0' + hex) + ' '); } return result.toLowerCase(); } - Badri
为什么你没有测试“类型化数组”?你的“base64ToHex”函数返回字符串,但是我的“str2ta”返回类型化数组。@Badri - Mohsen
我尝试了类型化数组、unit8buffer、int8buffer,尝试了所有这些之后才在这里发布了我的问题。很抱歉没有更清楚地说明我所尝试的输入。我也尝试了你的函数,但仍然出现相同的错误。我尝试了十六进制,因为来自节点的fs默认返回十六进制缓冲区,正如它在我的服务器代码中工作一样,我将其作为最后的手段尝试了一下。@Mohsen - Badri

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接