13

How can I get the size of the newly created new Image() in bytes if this image's src is base64 data image?

I have such coffeescript code:

# This string is received after some original image preprocessing
base64String = "data:image/jpeg;base64......"

newImageObj = new Image()
newImageObj.src = base64String
newImageObj.onload = ->
  console.log "Resized image width is " + this.width
  console.log "New file size in bytes is " + newImageObj.fileSize

The output is always like this:

Resized image width is 500
New file size in bytes is undefined

This newImageObj.fileSize is always undefined.

kovpack
  • 4,905
  • 8
  • 38
  • 55
  • 4
    In _Base64_, `4` chars represents `3` bytes and `=`s on the end let you know how many bytes short the final _String_ (so you don't carry `0`s) – Paul S. Apr 29 '15 at 09:34
  • It is sad that there is no built-in method for this, however, your approach works, so thank you :) – kovpack Apr 29 '15 at 10:12

6 Answers6

31

here is the algorithm to find file size from base64 string:

base64String = "data:image/jpeg;base64......";

var stringLength = base64String.length - 'data:image/png;base64,'.length;

var sizeInBytes = 4 * Math.ceil((stringLength / 3))*0.5624896334383812;
var sizeInKb=sizeInBytes/1000;
M Barzel
  • 739
  • 7
  • 9
  • 4
    very close :) 1024 – kovpack Apr 11 '18 at 16:55
  • 6
    What is the significance of the constants you are using 4, 3 and 0.5624896334383812? – shmit Jun 04 '18 at 18:27
  • I think var sizeInKb=sizeInBytes/1000; should be var sizeInKb=sizeInBytes/1024; – Abhay Jul 17 '20 at 15:12
  • 1
    Can you explain why 0.5624896334383812 ! ++ @paul-s – Vaisakh Rajagopal Jan 17 '21 at 19:26
  • In case, you want the result in Mb, so → var sizeInMb = sizeInBytes / 1048576; Thanks by the way – Roger Ospina Mar 06 '21 at 03:57
  • 12
    This formula is right for the wrong reason and thus overcomplicated. Base64 encodes 3 bytes of binary data on 4 characters. So to get the size of the original data, you juste have to multiply the stringLength (minus the header) by 3/4. The answer above made the mistake of multiplyijng by 4/3 instead. The 0.5624896334383812 magic constant is there to correct that error (4/3 * 0.5624896334383812 = 3/4). – elcye Sep 24 '21 at 17:11
  • 7
    The real formula is thus Math.ceil(stringLength / 4) * 3, minus 1 or 2 bytes if the string length is not a multiple of 4 or if it ends with padding, if you want to be extra precise. 1 missing char or one = at the end: 1 byte to remove. 2 missing chars or == at the end: 2 bytes to remove. It cannot have 3 missing chars or ends with ===. – elcye Sep 24 '21 at 17:44
7

I'd personally use something like this to do the above, which will work correctly in the case that the data isn't formatted correctly (eg, an '=' missing, which usually gets decoded correctly anyway)

new Buffer(base64String, 'base64').length
JD Byrnes
  • 783
  • 6
  • 17
1

I'm a little late to the question here, and maybe I'm wrong but...

Looking at your code above, it looks like you are defining a function and using arrow notation to return the answer. However, there are TWO lines following the arrow, and they aren't grouped in any way, so... the arrow notation is returning the value of onload() to the first log() only. So "this" actually has a value. But the second log() is a new command altogether, right? It's not being returned from onload(). So newImageObj is undefined.

Or maybe it's a coffeescript syntax, I'm not familiar with it. Would this fix it?

newImageObj = new Image()
newImageObj.src = base64String
newImageObj.onload = ->
{
  console.log "Resized image width is " + this.width
  console.log "New file size in bytes is " + newImageObj.fileSize
}
Todd Davis
  • 5,855
  • 10
  • 53
  • 89
1

You can refer to this example and see how base64 decoding works:


I have also attached the reference to base64 table. Base64 index table

Decoding->base64 string : QWJoaXNoZWs=


  1. First, you need to split the string character by character. Thus, you got 12 groups: Q W J o a X N o Z W s =

  2. Each group (character) is a Base64 character that has its own index, and now your task is to convert groups to indices. To do this, by mapping values from the Base64 Characters Table replace each character by its index (if you cannot find an index for a specific group, just discard it). All in all, you should get the following indices: 16 22 9 40 26 23 13 40 25 22 44

  3. At this step you should convert each group from decimal to binary. So find corresponding decimal values in the ASCII table and make sure you get the following binary values: 00010000 00010110 00001001 00101000 00011010 00010111 00001101 00101000 00011001 00010110 00101100

  4. Now remove the prefix “00” (two zeros) in front of each group: 010000 010110 001001 101000 011010 010111 001101 101000 011001 010110 101100

  5. There you have a simple concatenation of previous groups (that is, glue all the binary values together and get an 66-character string): 010000010110001001101000011010010111001101101000011001010110101100

  6. Then, divide the resulting string into groups so that each one has 8 characters (if the last group has less than 8 characters, you must discard it). Now you have 8 groups of eight-bit bytes: 01000001 01100010 01101000 01101001 01110011 01101000 01100101 01101011

  7. Once again using the ASCII table, convert all binary values into their ASCII characters: A b h i s h e k

  8. The final chord, concatenate all ASCII characters to get the result string: Abhishek

Thus,

Size of original string(in bytes) = floor(6n/8) – padding


Size of base64 string(in bytes) = ceil(8n/6) + padding


When decoding from base64

int paddingCount = (base64Data.endsWith("==")) ? 2 :(base64Data.endsWith("=")) ? 1 : 0;

double dataSize = floor(base64Data.length() * 3 / 4) - paddingCount;

When encoding to base64

int paddingCount = 3 - (stringToEncode.length()) % 3;

double dataSize = ceil(stringToEncode.length() * 4 / 3) + paddingCount;
-1

You can take advantage of the inner workings of fetch for that:

const url = 'data:image/jpeg;base64...'
const resp = await fetch(url)
const size = +resp.headers.get('Content-Length')
Nino Filiu
  • 16,660
  • 11
  • 54
  • 84
-1
const img = 'data:image/png;base64,aBdiVBORw0fKGgoAAA';

const buffer = Buffer.from(img.substring(img.indexOf(',') + 1));

console.log("Byte length: " + buffer.length);
console.log("MB: " + buffer.length / 1e+6);
Rahul
  • 1,554
  • 4
  • 16
  • 22