When I take content of a picture I try to dump it like that:
$filename = '(900).jpg';
$im = file_get_contents($filename);
var_dump(serialize($im));
When the picture is under 1mb everything works, but if it is more than 1mb browser crash can you tell me why is that a browser issue or some limitation of file_get_contents() function?
The only limitation of file_get_contents might be the memory which is allowed for PHP to use. And the default is about 128 MB.
It is a browser "issue" if you want to call it that. Outputting so much debug information to the browser is not a good idea as you can see. Additionally there is no benefit in viewing a binary file as text.
If you want to find out if the variable is set, you can use functions to check the size of the (binary) string e.g. mb_strlen().
A better way would be this
$filename = '(900).jpg';
$im = file_get_contents($filename);
// check if the file could be loaded
if ($im !== false) {
// start your processing
}
But this does not check what kind of file you have loaded into the string. If you must store the file into the database - which is considered very evil - you can either store the binary string into a BLOB type row or encode the binary string with base64_encode() and store it into a text type. Both of this solutions are also not recommended!
If you need to store image information into the database, you should think about using references to the files - e.g. the file path. Your primary objective is to secure that the database information and the filesystem information is always synchronized.
Related
I am writing some json results in files in PHP on shared hosting (fwrite).
Then I read those files to extract json results (file_get_contents).
It happens some times (maybe one out of more than one thousand) that when I read this file it appears truncated: I can only read a multiple of the first 32768 bytes of the file.
I added some code to copy/paste the file I am reading in case the json string is not valid, and I then get 2 different files: the original one was correctly written as it contains a valid json string and the copied one contains only the beginning of the original one and has a size of x*32768 bytes.
Would you have any idea of what could be the problem and how to solve this? (I don't know how to investigate further)
Thank you
Without example code it is impossible to give a 'fix my code' answer, but when doing file write/read sort of programming, you should follow a simple process (which, from the description, is missing one fairly critical step!)
First, write to a TEMP file (you are writing to a file, but it is important here to write to a TEMP file - otherwise, you could have race conditions....... ;);
an easy way to do that in php
$yourData = "whateverYourDataIs....";
$goodfilename = 'whateverYourGoodFileNameIsForYourData.json';
$tempfilename = 'tempfile' . time(); // MANY ways to do this (lots of SO posts on it - just get a unique name every time you write ('unique' may not be needed if you only occasionally do a write, but it is a good safety measure to avoid collisions and time() works for many programs.)
// Now, use $tempfilename in your fwrite.
$fwrite = fwrite($tempfilename,$yourData);
if ($fwrite === false) {
// the write failed, so do whatever 'error' function you may need
// since it failed, there should be no file, but not a bad idea to attempt to delete it
unlink ($tempfile);
}
else {
// the write succeeded, so let's do a 'sanity check' on the file to make sure it is good JSON (this is a 'paranoid' check, but "better safe than sorry", right?)
if(json_decode($tempfile)){
// we know the file is good JSON, so now RENAME (this is really fast, so collisions are almost impossible) NOTE: see http://php.net/manual/en/function.rename.php comments for some potential challenges and workarounds if you have trouble with rename.
rename($tempfilename,$goodfilename);
}
// Now, the GOOD file will contain your new data - and those read issues are gone! (though never say 'never' - it may be possible, but very unlikely!)
}
This may/not be your issue directly and you will have to suit this to fit your code, but as a safety factor - and a good way to avoid collisions, it should give you ~100% read success, which I believe is what you are after!)
If this doesn't help, then some direct code will be needed to provide a more complete answer.
As suggested by #UlrichEckhardt comment, it was due to read / write concurrency problem. I was trying to read a file that was being writen. I solved this by just waiting before trying to read the file again
i created a web service for sending files and that saved in server. The file format is zip. The file size may vary in the user need.
This is my code for before sending data in to server.
$filename = $_SERVER['DOCUMENT_ROOT'].'/data.zip';
$data = file_get_contents($filename);
$new_data = base64_encode($data);
This new_data variable send to the server.
This worked in all small files. But base64_encode return null when i use 5 mb file or larger file.
my problem is that base64_encode is not working in large string i genrated.
if any one know about this please help me.
The answer is simple: file_get_contents() is returning false which, when passed to base64_encode(), results in an empty string.
Why is file_get_contents() failing? It could be any number of reasons:
You don't have read permissions on the file. chmod() may help, but it's likely something caused elsewhere.
Memory limits prevent file_get_contents() from loading 5 MB of text into memory.
Filesystem or disk errors. (Unlikely but strictly possible.)
how safe the following code is, in case everybody could make use of it:
$file = $_FILES["file"]['tmp_name'];
$contents = file_get_contents($file);
$base64 = base64_encode($contents);
<img src="data:image/png;base64,'.$base64.'">
in case this is not safe agains hackers, etc. how should i secure it?
Char set of a base64 encoded string is ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx yz0123456789+/ and an = sign as a last byte filter.
So no matter what the file contents is, you are putting its base64 encoded value and even without escaping, browser will show the given string in the img tag.
You will however need to make sure that the string actually encodes to an image.
And by the way, you are wasting a lot of your server memory for this task by assigning a new variable in every step.
I have a sever which people can upload files to. The problem is that some of the filenames are mangled (dont have any extension) and so I cannot immediately determine file type. This question is two part: for the files which do have filenames what is the best way to determine whether or not it is an image? (Just a big long if/else if list?) Secondly, for the files which dont have extensions, how can I determine if they are images?
You can use exif_imagetype()
<?php
$type =exif_imagetype($image);
where $type is a value
IMAGETYPE_GIF
IMAGETYPE_JPEG
IMAGETYPE_PNG
IMAGETYPE_SWF
IMAGETYPE_PSD
IMAGETYPE_BMP
IMAGETYPE_TIFF_II (intel byte order)
IMAGETYPE_TIFF_MM (motorola byte order)
IMAGETYPE_JPC
IMAGETYPE_JP2
IMAGETYPE_JPX
IMAGETYPE_JB2
IMAGETYPE_SWC
IMAGETYPE_IFF
IMAGETYPE_WBMP
IMAGETYPE_XBM
IMAGETYPE_ICO
From the manual:
When a correct signature is found, the appropriate constant value will be returned otherwise the return value is FALSE. The return value is the same value that getimagesize() returns in index 2 but exif_imagetype() is much faster.
You can use getimagesize
It does not require the GD image library and it returns same information about image type.
http://it2.php.net/manual/en/function.getimagesize.php
If you have the GD2 extension enabled, you could just use that to load the file as an image, then if it returns invalid you can catch the error and return FALSE, otherwise return TRUE.
You have two options here, one's simple and pre-built with some shortfalls, the other is complex and requires math.
PHP's fileinfo can be used to detect file types based on the file's actual header information. For instance, I just grabbed your gravitar:
But the actual code is this:
‰PNG
IHDR szzô
IDATX…—OL\UÆZÀhëT)¡ c•1T:1‘Š‘.Ú(]4†A“ÒEY˜à.................................
So, even without the file name I could detect it quite obviously. This is what the PHP Fileinfo extension will do. Most PNG and JPG files tend to have this header in them, but this is not so for every single file type.
That being said, fileinfo is dead simple to use, from the manual:
$fi = new finfo(FILEINFO_MIME,'/usr/share/file/magic');
$mime_type = $fi->buffer(file_get_contents($file));
Your other option is more complex and it depends on your own personal ambitions, you could generate a histogram and profile files based on their content.
Something like this looks like a GIF file:
And something like this looks like a TIFF file:
From there you'd need to generate a model over multiple types of files for what the histogram of each type should be, and then use that to guess. This is a good method to use for files that don't really have those "magic headers" that can be read easily. Keep in mind, you'll need to learn some math and how to model an average histogram function and match them against files.
You can try to load the image into PHP's GD library, and see if it works.
$file = file_get_contents('file');
$img = imagecreatefromstring($file);
if($img === FALSE){
// file is NOT an image
}
else{
// file IS an image
}
Look at image magic identify. http://www.imagemagick.org/script/identify.php
The php wrapper is here: http://www.php.net/manual/en/function.imagick-identifyimage.php
Or if you just want to validate that it's an image (and don't care about the meta data): http://www.php.net/manual/en/function.imagick-valid.php
exif_imagetype() might work
make sure you have exif enabled.
Try looking at exif_imagetype
If you need a fast solution, use imagesx() and imagesy(). There is also a fast way to check large image file dimensions, by reading just a small amount of data from the file header. Explained in more detail in the following url:
http://hungred.com/useful-information/php-fastest-image-width-height/
You can use the Fileinfo extension:
http://www.php.net/manual/en/function.finfo-file.php
finfo_file() uses magic bytes and does not have to load the whole image into memory. The result is a string with the corresponding MIME type, e.g.:
text/html
image/gif
application/vnd.ms-excel
The type of the image is typically going to be able to be inferenced from the header information of the file.
For the first question is extension is known you could use the PHP function in_array() Documentation
My PHP web application has an API that can recieve reasonably large files (up to 32 MB) which are base64 encoded. The goal is to write these files somewhere on my filesystem. Decoded of course. What would be the least resource intensive way of doing this?
Edit: Recieving the files through an API means that I have a 32MB string in my PHP app, not a 32 MB source file somewhere on disk. I need to get that string decoded an onto the filesystem.
Using PHP's own base64_decode() isn't cutting it because it uses a lot of memory so I keep running into PHP's memory limit (I know, I could raise that limit but I don't feel good about allowing PHP to use 256MB or so per process).
Any other options? Could I do it manually? Or write the file to disk encoded and call some external command? Any thought?
Even though this has an accepted answer, I have a different suggestion.
If you are pulling the data from an API, you should not store the entire payload in a variable. Using curl or other HTTP fetchers you can automatically store your data in a file.
Assuming you are fetching the data through a simple GET url:
$url = 'http://www.example.com/myfile.base64';
$target = 'localfile.data';
$rhandle = fopen($url,'r');
stream_filter_append($rhandle, 'convert.base64-decode');
$whandle = fopen($target,'w');
stream_copy_to_stream($rhandle,$whandle);
fclose($rhandle);
fclose($whandle);
Benefits:
Should be faster (less copying of huge variables)
Very little memory overhead
If you must grab the data from a temporary variable, I can suggest this approach:
$data = 'your base64 data';
$target = 'localfile.data';
$whandle = fopen($target,'w');
stream_filter_append($whandle, 'convert.base64-decode',STREAM_FILTER_WRITE);
fwrite($whandle,$data);
fclose($whandle);
Decode the data in smaller chunks. Four characters of Base64 data equal three bytes of “Base256” data.
So you could group each 1024 characters and decode them to 768 octets of binary data:
$chunkSize = 1024;
$src = fopen('base64.data', 'rb');
$dst = fopen('binary.data', 'wb');
while (!feof($src)) {
fwrite($dst, base64_decode(fread($src, $chunkSize)));
}
fclose($dst);
fclose($src);
It's not a good idea transfer 32Mb string. But I have a solution for my task, that can accept any size files from browser to app server. Algorithm:
Client
Javascript: Read file from INPUT with FileReader and readAsDataURL() to FILE var.
Cut all of data in FILE from start to first "," position Split it with array_chunks by max_upload_size/max_post_size php var.
Send chunk with UID, chunk number and chunks quantity and wait for response, then send another chunk one by one.
Server Side
Write each chunk until the last one. Then do base64 with streams:
$src = fopen($source, 'r');
$trg = fopen($target, 'w');
stream_filter_append($src, 'convert.base64-decode');
stream_copy_to_stream($src, $trg);
fclose($src);
fclose($trg);
... now you have your base64 decoded file in $target local path. Note! You can not read and write the same file, so $source and $target must be different.