最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Determine if a base64 string or a buffer contains JPEG or PNG without metadata? Possible? - Stack Overflow

programmeradmin3浏览0评论

Is there any way to do this using node, whether natively or with a plugin?

What I'm trying to accomplish is to choose loseless or lossy image compression depending on the input type. Loseless on a large JPEG is a storage catastrophe.

Is there any way to do this using node, whether natively or with a plugin?

What I'm trying to accomplish is to choose loseless or lossy image compression depending on the input type. Loseless on a large JPEG is a storage catastrophe.

Share Improve this question edited Aug 6, 2019 at 23:45 Slbox asked Apr 5, 2018 at 5:12 SlboxSlbox 13.1k16 gold badges64 silver badges131 bronze badges 2
  • Where is the base64 data coming from, and why doesn't the source tell you what type the data is? – loganfsmyth Commented Apr 5, 2018 at 6:55
  • Hey Mark, I'm simply not sure yet if it solves my issue as I got pulled in a different direction. I am thinking maybe no, because I'm often dealing with clipboard data, and I'm not sure that it will have the leading bytes that identify the data, but I can't be sure yet. Thank you for your answer though. If it solves my problem, or if I don't get to it soon, as it's the best answer here, I will come back to mark it answered. Thanks again. – Slbox Commented Apr 9, 2018 at 16:52
Add a comment  | 

3 Answers 3

Reset to default 15

The first eight bytes of a PNG file always contain the following values - see PNG Specification:

(decimal)              137  80  78  71  13  10  26  10
(hexadecimal)           89  50  4e  47  0d  0a  1a  0a
(ASCII C notation)    \211   P   N   G  \r  \n \032 \n

So, if I take 8 bytes from the start of any PNG file and base64 encode it as follows, I get:

head -c8 test.png | base64
iVBORw0KGgo=

The first 2 bytes of every JPEG file contain ff d8 in hex - see Wikipedia entry for JPEG. So if I take any JPEG file and base64 encode the first two bytes as follows, I get:

head -c2 test.jpg | base64
/9g=

So my suggestion would be to look at the first few (10 for PNG and 2 for JPEG, always excluding the =) characters of your base64-encoded file and see if they match what I am suggesting and then use that as the determinant - be sure to output error messages if your string matches neither in case the test is not sufficiently thorough for some reason!


Why 10 characters for PNG? Because the guaranteed signature is 8 bytes, i.e. 64 bits and base64 splits into 6 bits at a time to generate a character, so the first 10 characters are the first 60 bits. The 11th character will vary depending on what follows the signature.

Same logic for JPEG... 2 bytes is 16 bits, which means 2 characters each corresponding to 6 bits are guaranteed. The 3rd character will vary depending on what follows the 2-byte SOI marker.

@MarkSetchell's answer above is correct in theory. However, in practice, it doesn't work for JPGs!

It is true that head -c2 test.jpg | base64 produces /9g

However

head -c3 test.jpg | base64`
/9j/

So, if you want to "Determine if a base64 string or a buffer contains JPEG" you need to test that it starts with /9j not /9g!

Checking magic number headers does not check if an image is corrupted.

Png files have a specified internal structure with chunks that have crc error checking codes so you can check for png file corruption. I have a tiny npm library for doing this here: https://www.npmjs.com/package/png-validator

It checks that all internal header and data chunks are valid. So you can be very sure you are dealing with a valid png file if you do this.

The only js implementation that does any deep scan on jpgs that I've found is this: https://github.com/image-size/image-size/blob/main/lib/types/jpg.ts

I haven't tried adapting it.

In the browser you can use the Image constructor to load an image, and the image instance will emit an error event if the browser fails to load the image or a load event if it is successfull.

const image = new Image();
image.src = url;
const is_valid = await new Promise((resolve, reject) => {
  image.addEventListener("error", () => resolve(false));
  image.addEventListener("load", () => resolve(true));
});

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论