I'm using Valum's file uploader to upload images with AJAX. This script submits the file to my server in a way that I don't fully understand, so it's probably best to explain by showing my server-side code:
$pathToFile = $path . $filename;
//Here I get a file not found error, because the file is not yet at this address
getimagesize($pathToFile);
$input = fopen('php://input', 'r');
$temp = tmpfile();
$realSize = stream_copy_to_stream($input, $temp);
//Here I get a string expected, resource given error
getimagesize($input);
fclose($input);
$target = fopen($pathToFile, 'w');
fseek($temp, 0, SEEK_SET);
//Here I get a file not found error, because the image is not at the $target yet
getimagesize($pathToFile);
stream_copy_to_stream($temp, $target);
fclose($target);
//Here it works, because the image is at the desired location so I'm able to access it with $pathToFile. However, the (potentially) malicious file is already in my server.
getimagesize($pathToFile);
The problem is that I want to perform some file validation here, using getimagesize(). getimagesize only supports a string, and I only have resources available, which result in the error: getimagesize expects a string, resource given.
It does work when I perform getimagesize($pathTofile) at the end of the script, but then the image is already uploaded and the damage could already have been done. Doing this and performing the check afterwards and then maybe deleting te file seems like bad practice to me.
The only thing thats in $_REQUEST is the filename, which i use for the var $pathToFile. $_FILES is empty.
How can I perform file validation on streams?
EDIT:
the solution is to first place the file in a temporary directory, and perform the validation on the temporary file before copying it to the destination directory.
// Store the file in tmp dir, to validate it before storing it in destination dir
$input = fopen('php://input', 'r');
$tmpPath = tempnam(sys_get_temp_dir(), 'upl'); // upl is 3-letter prefix for upload
$tmpStream = fopen($tmpPath, 'w'); // For writing it to tmp dir
stream_copy_to_stream($input, $tmpStream);
fclose($input);
fclose($tmpStream);
// Store the file in destination dir, after validation
$pathToFile = $path . $filename;
$destination = fopen($pathToFile, 'w');
$tmpStream = fopen($tmpPath, 'r'); // For reading it from tmp dir
stream_copy_to_stream($tmpStream, $destination);
fclose($destination);
fclose($tmpStream);
PHP 5.4 now supports getimagesizefromstring
See the docs:
http://php.net/manual/pt_BR/function.getimagesizefromstring.php
You could try:
$input = fopen('php://input', 'r');
$string = stream_get_contents($input);
fclose($input);
getimagesizefromstring($string);
Instead of using tmpfile() you could make use of tempnam() and sys_get_temp_dir() to create a temporary path.
Then use fopen() to get a handle to it, copy over the stream.
Then you've got a string and a handle for the operations you need to do.
//Copy PHP's input stream data into a temporary file
$inputStream = fopen('php://input', 'r');
$tempDir = sys_get_temp_dir();
$tempExtension = '.upload';
$tempFile = tempnam($tempDir, $tempExtension);
$tempStream = fopen($tempFile, "w");
$realSize = stream_copy_to_stream($inputStream, $tempStream);
fclose($tempStream);
getimagesize($tempFile);
Related
In a symfony Controller I have a file (UploadedFile object) as a variable.
I want to open this "variable" with php ZipArchive, and extract it.
But the open() methods is expecting a string, which is the filename in the filesystem. Is there any way to process the file with the ZipArchive and without writing the file variable to the FS?
You can use tmpfile() to create a temporary file, write to it and then use it in the zip. Example:
<?php
$zip = new ZipArchive();
$zip->open(__DIR__ . '/zipfile.zip', ZipArchive::CREATE);
$fp = tmpfile();
fwrite($fp, 'Test');
$filename = stream_get_meta_data($fp)['uri'];
$zip->addFile($filename, 'filename.txt');
$zip->close();
fclose($fp);
Little improve:
$zipContent; // in this variable could be ZIP, DOCX, XLSX etc.
$fp = tmpfile();
fwrite($fp, $zipContent);
$stream = stream_get_meta_data($fp);
$filename = $stream['uri'];
$zip = new ZipArchive();
$zip->open($filename);
// profit!
$zip->close();
fclose($fp);
Just make no sense to create "zipfile.zip" and add file inside, because we already have a variable.
Check out this answer at https://stackoverflow.com/a/53902626/859837
There is a way to create a file which resides in memory only.
I try to use ftp_fput in PHP. It gives me an error like:
550 Filename invalid.
I am using code like this way:
// $ftp_conn is connection string.
// Generate temp file
$temp = tmpfile();
fwrite($temp, $temp_css_data); // $temp_css_data = file data
fseek($temp, 0, SEEK_SET);
ftp_fput($ftp_conn, $dest, $temp, FTP_BINARY); // $dest is remote server path
// $dest = "D:/xampp/htdocs/sites/1890/style.css";
Destination path is proper because its create folder there but not put file into folder.
I am using uploadifive to upload images - however I want to change the name of the images to "newimage" I am not sure where or how to do this with what is provided - this is the last modification I need to deploy.
You're going to need to do the name change in the PHP script that handles the uploading (I'm assuming your using PHP since that's uplidfy's standard). It can be a bit tricky because you have to separate the incoming file name from it's extension. This should be enough to get you started.
$theFile = $_FILES['file_upl']['name'];
$tempFile = $_FILES['file_upl']['tmp_name'];
$newName = "newname";
$saveDir = $_SERVER['DOCUMENT_ROOT']."/images/";
//Function returns the extension from the '.' on
function get_ext($from_file) {
$ext = strtolower(strrchr($from_file,'.'));
return $ext;
}
//This will rename the file to $newname + the extension
$theFile = $newname.get_ext($theFile);
//Save the file
move_uploaded_file($tempFile,$saveDir.$theFile);
$file = 'uploaded_img.jpg';
$remote_file = 'Saved.txt';
ftp_put($conn_id, $remote_file, $file, FTP_ASCII)
just change the name of the remote file
below is the code which i want to modify
$input = fopen("php://input", "r");
$temp = tmpfile();
$realSize = stream_copy_to_stream($input, $temp);
fclose($input);
if ($realSize != $this->getSize()){
return false;
}
$target = fopen($path, "w");
fseek($temp, 0, SEEK_SET);
stream_copy_to_stream($temp, $target);
fclose($target);
I want to save the contents into the memory and transfer it accross to other server without saving it on apache server.
when i try to output the contents i only see resource id# 5. Any suggestion, comments are highly apprecited . thanks
The code you have opens file handles, which in themselves are not the content. To get the content into a variable, just read it like any other file:
$put = file_get_contents('php://input');
To get the contents of the stream:
rewind($temp); // rewind the stream to the beginning
$contents = stream_get_contents($temp);
var_dump($contents);
Or, use file_get_contents as #deceze mentions.
UPDATE
I noticed you're also opening a temp file on disk. You might want to consider simplifying your code like so:
$put = stream_get_contents(STDIN); // STDIN is an open handle to php://input
if ($put) {
$target = fopen('/storage/put.txt', "w");
fwrite($target, $put);
fclose($target);
}
I would like to download a zip archive and unzip it in memory using PHP.
This is what I have today (and it's just too much file-handling for me :) ):
// download the data file from the real page
copy("http://www.curriculummagic.com/AdvancedBalloons.kmz", "./data/zip.kmz");
// unzip it
$zip = new ZipArchive;
$res = $zip->open('./data/zip.kmz');
if ($res === TRUE) {
$zip->extractTo('./data');
$zip->close();
}
// use the unzipped files...
Warning: This cannot be done in memory — ZipArchive cannot work with "memory mapped files".
You can obtain the data of a file inside a zip-file into a variable (memory) with file_get_contentsDocs as it supports the zip:// Stream wrapper Docs:
$zipFile = './data/zip.kmz'; # path of zip-file
$fileInZip = 'test.txt'; # name the file to obtain
# read the file's data:
$path = sprintf('zip://%s#%s', $zipFile, $fileInZip);
$fileData = file_get_contents($path);
You can only access local files with zip:// or via ZipArchive. For that you can first copy the contents to a temporary file and work with it:
$zip = 'http://www.curriculummagic.com/AdvancedBalloons.kmz';
$file = 'doc.kml';
$ext = pathinfo($zip, PATHINFO_EXTENSION);
$temp = tempnam(sys_get_temp_dir(), $ext);
copy($zip, $temp);
$data = file_get_contents("zip://$temp#$file");
unlink($temp);
As easy as:
$zipFile = "test.zip";
$fileInsideZip = "somefile.txt";
$content = file_get_contents("zip://$zipFile#$fileInsideZip");
Old subject but still relevant since I asked myself the same question, without finding an answer.
I ended up writing this function which returns an array containing the name of each file contained in the archive, as well as the decompressed contents of that file:
function GetZipContent(String $body_containing_zip_file) {
$sectors = explode("\x50\x4b\x01\x02", $data);
array_pop($sectors);
$files = explode("\x50\x4b\x03\x04", implode("\x50\x4b\x01\x02", $sectors));
array_shift($files);
$result = array();
foreach($files as $file) {
$header = unpack("vversion/vflag/vmethod/vmodification_time/vmodification_date/Vcrc/Vcompressed_size/Vuncompressed_size/vfilename_length/vextrafield_length", $file);
array_push($result, [
'filename' => substr($file, 26, $header['filename_length']),
'content' => gzinflate(substr($file, 26 + $header['filename_length'], -12))
]);
}
return $result;
}
Hope this is useful ...
You can get a stream to a file inside the zip and extract it into a variable:
$fp = $zip->getStream('test.txt');
if(!$fp) exit("failed\n");
while (!feof($fp)) {
$contents .= fread($fp, 1024);
}
fclose($fp);
If you can use system calls, the simplest way should look like this (bzip2 case). You just use stdout.
$out=shell_exec('bzip2 -dkc '.$zip);