2

I have an array bit like this:

[255, 216, 255, 224, 0, 16, 74, 70, 73, 70, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 255, 219, 0, 67, 0, 1, 1, ...]

And I need to convert this into an actual image.

So I followed this python script that does the same and works properly:

file = x['data']['file']['file'] 

listRes = file.replace(' ', '')
listRes = listRes.replace('[', '').replace(']', '')
listRes = listRes.split(',')
for i in range(len(listRes)):
    listRes[i] = int(listRes[i])
f = open('x.png', 'wb')
f.write(bytes(listRes))
f.close()

And this is my similar php code:

if(!empty($que["answer"]["file"])){
    $strbit = str_replace("[","",$que["answer"]["file"]["file"]);
    $strbit = str_replace("]","",$strbit);
    $strbit = str_replace(" ","",$strbit);
    $arrbit = explode(',', $strbit);
    for($j=0;$j<count($arrbit);$j++){
        $arrbit[$j] = (int)($arrbit[$j]);
    }
    $imageName = str_random(10).'.'.'png';
    \File::put(storage_path(). '/images/' . $imageName, ($arrbit));
}

But now the problem is that the exported image looks like this:

capture

Now instead of \File::put(storage_path()... I tried using file_put_contents($imageName, pack("C*", ...$arrbit)); but it does not export anything at all!

So what's going wrong here? How can I solve this issue?


UPDATE #1:

I tried this:

if(!empty($que["answer"]["file"])){
        $strbit = str_replace("[","",$que["answer"]["file"]["file"]);
        $strbit = str_replace("]","",$strbit);
        $strbit = str_replace(" ","",$strbit);
        $arrbit = explode(',', $strbit);
        for($j=0;$j<count($arrbit);$j++){
            $arrbit[$j] = (int)($arrbit[$j]);
        }
        $imageName = str_random(10).'.'.'jpeg';
        file_put_contents($imageName, pack("C*", ...$arrbit));
    }

But does not export any image and also not return any error!

Pouya
  • 114
  • 1
  • 8
  • 36
  • You remove commas, while Python code splits string into array of numeric strings by comma. As to how write it to the disk, see the first answer to [this](https://stackoverflow.com/questions/44033739/how-to-write-array-with-unsigned-byte-values-to-a-binary-file-in-php-5-6) question. – alx Sep 05 '22 at 08:39
  • I dont see the python code doing any base64 decoding, and the python code is saving as a mp4 and you're saving as a png – Dale Sep 05 '22 at 08:57
  • @alx I have updated the question would you mind check it out – Pouya Sep 05 '22 at 09:30
  • @Dale I have updated the question would you mind check it out – Pouya Sep 05 '22 at 09:31
  • In your last line of php code try this `\File::put(storage_path(). '/images' . $imageName, (implode('', $arrbit)));` I suspect if you open the file in a text editor you will the word `Array` – Dale Sep 05 '22 at 09:35
  • @Dale I tried that and again shows me the same small square that is shown in the question! I'm really stuck with this :( – Pouya Sep 05 '22 at 09:40
  • probably need to find out what that python function `bytes` is doing and replicate that in php – Dale Sep 05 '22 at 09:43
  • @Dale It is similar to `chr` but chr() expects parameter 1 to be int, and in this case it's array given! – Pouya Sep 05 '22 at 09:46
  • Please never present your sample data as a screenshot. We prefer array data to be provided as `var_export()` output. Doesn't Python have a json decoding function?!? PHP does have `array_map('intval', json_decode($que["answer"]["file"]["file"], true))` – mickmackusa Sep 05 '22 at 09:53
  • @mickmackusa I removed the screenshot and added the data. And the python does not have any other function. – Pouya Sep 05 '22 at 09:56
  • I think my comment still valid. Just tried this right now: `$arr = [255, 216, 255, 224, 0, 16, 74, 70, 73, 70, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 255, 219, 0, 67, 0, 1, 1, /* ... */]; var_dump(pack("C*", ...$arr));`, and output looks like JPEG (not PNG! it has "JFIF" header). So, given `$arr` is an array of numbers, doing the following will yield you a valid file: `file_put_contents('img.png', pack("C*", ...$arr));`. Make sure format matches extension, tho. – alx Sep 05 '22 at 10:22
  • I see you edited your question, but you still don't use spread operator in `pack()` call. It is required, you need to write `pack("C*", ...$arrbit)`, otherwise it will try to render whole array of numbers as a single character. – alx Sep 05 '22 at 11:40
  • @alx I run this code but don't export any image – Pouya Sep 05 '22 at 11:41
  • Please see my comment above, you need to use spread operator: `pack("C*", ...$arrbit)`. – alx Sep 05 '22 at 11:42
  • @alx I dont get what you mean by spread operator – Pouya Sep 05 '22 at 11:43
  • @alx Now I'm using this line: `file_put_contents($imageName, pack("C*", ...$arrbit));` but does not export any image – Pouya Sep 05 '22 at 11:44
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/247790/discussion-between-pouya-and-alx). – Pouya Sep 05 '22 at 11:44
  • @alx Finally it worked out bro!! I tried changing the path and it worked out `file_put_contents(storage_path(). '/app/public/application/' . $imageName, pack("C*", ...$arrbit));` HOORA – Pouya Sep 05 '22 at 11:55
  • @alx thanks man! You can add that as answer if you would like to. Also it would be best if you add some refrence to spread operator and `pack` that is used in file put contents. I haven't seen them till now – Pouya Sep 05 '22 at 11:58
  • I see at least [this dupe](https://stackoverflow.com/q/29919260/2943403) which points to [this page](https://stackoverflow.com/q/5473011/2943403) – mickmackusa Sep 05 '22 at 12:13
  • Okay, glad to help, let me prepare a full answer now. – alx Sep 05 '22 at 13:26

1 Answers1

3

So, main issue was: how to convert input format -- string of comma separated character codes in brackets -- into binary form and write it to the file.

PHP has pack() function which fits this purpose nicely. Specifically, we need C* format as we are going to output raw 8-bit characters. So, given we have this input:

$arr = [255, 216, 255, 224, 0, 16, 74, 70, 73, 70, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 255, 219, 0, 67, 0, 1, 1, /* ... */];

This is how we can convert it into a binary string (note the ... token -- it is very important here, as pack() expects one argument per character converted, not an array as a single argument):

$str = pack("C*", ...$arr);

Finally, original question was asking about .png files, but apparently the example data has JFIF header, which indicates .jpg format. If format is not known, one can use getimagesize() to guess it (note: the function requires file already written to disk, not a binary string with file contents).

alx
  • 2,314
  • 2
  • 18
  • 22