PHP - Renaming a file to disallow duplicates - php

So I am using this script to upload a file to a directory and show it live.
<?php
function UploadImage($settings = false)
{
// Input allows you to change where your file is coming from so you can port this code easily
$inputname = (isset($settings['input']) && !empty($settings['input']))? $settings['input'] : "fileToUpload";
// Sets your document root for easy uploading reference
$root_dir = (isset($settings['root']) && !empty($settings['root']))? $settings['root'] : $_SERVER['DOCUMENT_ROOT'];
// Allows you to set a folder where your file will be dropped, good for porting elsewhere
$target_dir = (isset($settings['dir']) && !empty($settings['dir']))? $settings['dir'] : "/uploads/";
// Check the file is not empty (if you want to change the name of the file are uploading)
if(isset($settings['filename']) && !empty($settings['filename']))
$filename = $settings['filename'] . "sss";
// Use the default upload name
else
$filename = preg_replace('/[^a-zA-Z0-9\.\_\-]/',"",$_FILES[$inputname]["name"]);
// If empty name, just return false and end the process
if(empty($filename))
return false;
// Check if the upload spot is a real folder
if(!is_dir($root_dir.$target_dir))
// If not, create the folder recursively
mkdir($root_dir.$target_dir,0755,true);
// Create a root-based upload path
$target_file = $root_dir.$target_dir.$filename;
// If the file is uploaded successfully...
if(move_uploaded_file($_FILES[$inputname]["tmp_name"],$target_file)) {
// Save out all the stats of the upload
$stats['filename'] = $filename;
$stats['fullpath'] = $target_file;
$stats['localpath'] = $target_dir.$filename;
$stats['filesize'] = filesize($target_file);
// Return the stats
return $stats;
}
// Return false
return false;
}
?>
<?php
// Make sure the above function is included...
// Check file is uploaded
if(isset($_FILES["fileToUpload"]["name"]) && !empty($_FILES["fileToUpload"]["name"])) {
// Process and return results
$file = UploadImage();
// If success, show image
if($file != false) { ?>
<img src="<?php echo $file['localpath']; ?>" />
<?php
}
}
?>
The thing I am worried about is that if a person uploads a file with the same name as another person, it will overwrite it. How would I go along scraping the filename from the url and just adding a random string in place of the file name.
Explanation: When someone uploads a picture, it currently shows up as
www.example.com/%filename%.png.
I would like it to show up as
www.example.com/randomstring.png
to make it almost impossible for images to overwrite each other.
Thank you for the help,
A php noob

As contributed in the comments, I added a timestamp to the end of the filename like so:
if(isset($settings['filename']) && !empty($settings['filename']))
$filename = $settings['filename'] . "sss";
// Use the default upload name
else
$filename = preg_replace('/[^a-zA-Z0-9\.\_\-]/',"",$_FILES[$inputname]["name"]) . date('YmdHis');
Thank you for the help

Related

Codeigniter 4: Uploading files with move_uploaded_file

I just started moving CodeIgniter 3 project to CodeIgniter 4.
Everything works fine except file upload.
I would like to keep the user uploaded files in /writable/uploads. Below is the code I use to move the uploaded file to desired location.
$target_dir = '/writable/uploads/recordings/';
$target_file = $target_dir . basename($_FILES["gfile"]["name"]);
$FileType = pathinfo($target_file,PATHINFO_EXTENSION);
if($FileType != "mp3") {
$vmuploadOk = 1;
}
else
$vmuploadOk = 1;
if ($vmuploadOk == 1) {
$greetfile = $id . "g" . basename($_FILES["gfile"]["name"]);
$target_filenew = $target_dir . $greetfile;
move_uploaded_file($_FILES["gfile"]["tmp_name"], $target_filenew);
}
I assume that it is because CI4 keeps writable folder outside public folder.
You are not using CodeIgniter's built in functions. Everything shown in your code are PHP functions. If you want to leverage built in CI functions, then look through the documentation as linked by #Boominathan Elango.
To get the file from the request:
$file = $this->request->getFile('here_goes_input_name');
As specified here
To move the file using CI function:
$file->move(WRITEPATH.'uploads', $newName);
As specified here
This worked for me and I hope it will also work for you. In codeigniter 4 please use this to upload your files and move it in your controller.
if($imagefile = $this->request->getFiles())
{
if($img = $imagefile['gfile'])
{
if ($img->isValid() && ! $img->hasMoved())
{
$newName = $img->getRandomName(); //This is if you want to change the file name to encrypted name
$img->move(WRITEPATH.'uploads', $newName);
// You can continue here to write a code to save the name to database
// db_connect() or model format
}
}
}
OR
if($img = $this->request->getFile('gfile'))
{
if ($img->isValid() && ! $img->hasMoved())
{
$newName = $img->getRandomName();
$img->move(ROOTPATH . 'public/uploads/images/users', $newName);
// You can continue here to write a code to save the name to database
// db_connect() or model format
}
}
Then in your html input field
<input type="file" name="gfile">
I hope this works else call my attention

Upload fails "move uploaded file"

First off, the upload folder is given 777, and my old upload script works, so the server accepts files. How ever this is a new destination.
I use krajee bootstrap upload to send the files. And I receive a Jason response. The error seems to be around move uploaded file. I bet it's a simple error from my side, but I can't see it.
<?php
if (empty($_FILES['filer42'])) {
echo json_encode(['error'=>'No files found for upload.']);
// or you can throw an exception
return; // terminate
}
// get the files posted
$images = $_FILES['filer42'];
// a flag to see if everything is ok
$success = null;
// file paths to store
$paths= [];
// get file names
$filenames = $images['name'];
// loop and process files
for($i=0; $i < count($filenames); $i++){
$ext = explode('.', basename($filenames[$i]));
$target = "uploads" . DIRECTORY_SEPARATOR . md5(uniqid()) . "." . array_pop($ext);
if(move_uploaded_file($_FILES["filer42"]["tmp_name"][$i], $target)) {
$success = true;
$paths[] = $target;
} else {
$success = false;
break;
}
}
// check and process based on successful status
if ($success === true) {.
$output = [];
$output = ['uploaded' => $paths];
} elseif ($success === false) {
$output = ['error'=>'Error while uploading images. Contact the system administrator'];
// delete any uploaded files
foreach ($paths as $file) {
unlink($file);
}
} else {
$output = ['error'=>'No files were processed.'];
}
// return a json encoded response for plugin to process successfully
echo json_encode($output);
?>
I think field name is the issue. Because you are getting image name with filer42 and upload time, you are using pictures.
Please change
$_FILES["pictures"]["tmp_name"][$i]
to
$_FILES["filer42"]["tmp_name"][$i]
And check now, Hope it will work. Let me know if you still get issue.
The error is not in this script but in the post.
I was using <input id="filer42" name="filer42" type="file">
but it have to be <input id="filer42" name="filer42[]" type="file" multiple>
as the script seems to need an arrey.
It works just fine now.

PHP - Simple script for image compression with pngquant

:) i found this 1 line of code in another post which successfully compresses the image using pngquant. the thing is, it outputs the optimised image with a different name (obviously to preserve the original).
im trying to find a way to:
a) add a minimum quality parameter of 60
b) use an if/else statement to to allow the user to choose to overwrite the existing file or output a new optimised image (of a user specified name)
thank you! ntlri - not to long read it
<?php system('pngquant --quality=85 image.png'); ?>
so what i have tried is the following.. for some reason the single quotes need to be double quotes to parse the variables correctly..
<?php
$min_quality = 60; $max_quality = 85;
$keep_original = 'dont_keep';
if ($keep_original == 'keep') {
$image_name = 'image.png';
$path_to_image = 'images/' . $image_name;
$new_file = 'image2.png';
$path_to_new_image = 'images/' . $new_file;
// don't know how to output to specified $new_file name
system("pngquant --quality=$min_quality-$max_quality $path_to_image");
} else {
$image_name = 'image.png';
$path_to_image = 'images/' . $image_name;
// don't know if you can overwrite file by same name as additional parameter
system("pngquant --quality=$min_quality-$max_quality $path_to_image");
// dont't know how you get the name of the new optimised image
$optimised_image = 'images/' . $whatever_the_optimised_image_is_called;
rename($optimised_image, $image_name);
unlink($optimised_image);
}
?>
from the docs of this program :
The output filename is the same as the input name except that\n\ it
ends in \"-fs8.png\", \"-or8.png\" or your custom extension
so , for this question:
// don't know how to output to specified $new_file name
system("pngquant --quality=$min_quality-$max_quality $path_to_image");
to choose a new name, assume you are compress image name.png :
--ext=_x.png
this will create new image called name_x.png
so , your $new_file would be just a suffix ,
$new_file = '_x.png'; // to choose new file name name_x.png
// don't know if you can overwrite file by same name as additional
parameter
as mentioned in the program docs , the new file name will be suffixed by either -fs8.png or -or8.png , so you may rename the file which will produced with this suffix , OR simply set the --ext option to : .png and this will append to the original file
--ext=.png
for more details, check the repository
i spoke to pornel whos the chappie that developed pngquant. its actually a lot simpler than all that i wrote that before...
! important - it is very important to use escapeshellarg() else people can take over your server by uploading a file with a special filename apparently.
$image_name = 'image.png';
$target_file = 'images/' . $image_name;
$existing_image = 'image.png'; // image already on server if applicable
$keep = 'keep';
$target_escaped = escapeshellarg($target_file);
if ($keep == 'keep') {
// process/change output file to image_compressed.png keeping both images
system("pngquant --force --quality=70 $target_escaped --ext=_compressed.png");
$remove_ext = substr($newFileName, 0 , (strrpos($newFileName, ".")));
// $new_image is just the name (image_compressed.png) if you need it
$new_image = $remove_ext . '_compressed.png';
// remove old file if different name
if ($existing_image != $newFileName) {
$removeOld = '../images/' . $existing_image;
unlink($removeOld);
} // comment out if you want to keep existing file
} else {
// overwrite if file has the same name
system("pngquant --force --quality=70 $target_escaped --ext=.png");
// remove old file if different name
if ($existing_image != $newFileName) {
$removeOld = '../images/' . $existing_image;
unlink($removeOld);
}
$new_image = $newFileName;
}
to override same name use this command
pngquant.exe --ext=.png --force input.png
so the output name will remain input.png

How to validate an image before saving in a file

I have a function that saves the image in the respective folder.
I want to check if the image already exist in the folder , first with name if the name are same then with the SHA1 of the image
My current function looks something like this
function descargarImagen($id,$url,$pais1)
{
$ruta = rutaBaseImagen($id,$_POST['pais']);
$contenido = file_get_contents($url);
$ext = explode('.', $url);
$extension = $ext[count($ext) -1];
$imagen['md5'] = md5($contenido);
$imagen['sha1'] = sha1($contenido);
$imagen['nombre'] = $imagen['md5'].'.'.$extension;
$ficherof = $pais[$pais1]['ruta_imagenes'].'/'.$ruta.'/'.$imagen['nombre'];
$ficherof = str_replace('//', '/', $ficherof);
//Here before putting the file I want to check if the image already exist in the folder
file_put_contents($ficherof, $contenido);
$s = getimagesize($ficherof);
$imagen['mime'] = $s["mime"];
$imagen['size'] = $s[0].'x'.$s[1];
$imagen['url'] = $urlbase_imagenes.'/'.$ruta.'/'.$imagen['nombre'];
$imagen['url'] = str_replace('//', '/', $imagen['url']);
$imagen['date'] = time();
$imagen['fichero'] = $ficherof;
return $imagen;
}
I am trying to validate in two ways
1.If the name of the image is present in the folder or not .
2.If present then check if the image is same or different , because there could be new image but same name
Any suggestion will be appreciated
Thanks in Advance
You can use this,
// Check if file already exists
if (file_exists($target_file)) {
//echo "Sorry, file already exists.";
/**
* validate images that are different but have same name
* we can use base64_encode function to compare the old and new image
* basics: http://php.net/manual/en/function.base64-encode.php
* online conversion help: http://freeonlinetools24.com/base64-image
*/
$base64_encoded_new_image=base64_encode(file_get_contents($name_of_your_new_image_that_you_want_to_upload));
$base64_encoded_old_image=base64_encode(file_get_contents($name_of_the_image_that_already_exists));
if ($base64_encoded_new_image!=$base64_encoded_old_image) {
/** so the images are actually not same although they have same name
* now do some other stuffs
*/
}
}
for more information please visit http://www.w3schools.com/php/php_file_upload.asp
The hint given by this comment:
//Here before putting the file I want to check if the image already exist in the folder
makes me think you're asking for file_exists, which is the first thing that's shown if you search for "php file exist" in any search engine...

display the content of a folder

I have the following PHP code to display the content of a directory in my website:
<?php
$conn = ftp_connect("host") or die("Could not connect");
ftp_login($conn,"username","password");
if ($_GET['dir'] != null) {
ftp_chdir($conn, "logs/{$_GET['dir']}");
}
else
{
ftp_chdir($conn, "logs");
}
$files = ftp_nlist($conn,"");
foreach($files as $value) {
echo "{$value}";
}
ftp_close($conn);
?>
This is my webpage
When I click on the subdirectory1 or subdirectory2, I get the content of it (some images). Then when I click on one of the images, I get the content of the root directory of my website.
How can I display only the image when a visitor clicks on it? Note that I don't want to download it or anything - I just want to display it in the browser when a visitor clicks on it.
You need to establish which of the returned items are files and which are directories. For that you are better of using ftp_rawlist as it allows to extract more data. Then create links for each case and so you can process them appropriately. Here's a possible implementation:
$ftpHost = 'hostname';
$ftpUser = 'username';
$ftpPass = 'password';
$startDir = 'logs';
if (isset($_GET['file'])) {
// Get file contents (can also be fetched with cURL)
$contents = file_get_contents("ftp://$ftpUser:$ftpPass#$ftpHost/$startDir/" . urldecode($_GET['file']));
// Get mime type (requires PHP 5.3+)
$finfo = new finfo(FILEINFO_MIME);
$mimeType = $finfo->buffer($contents);
// Set content type header and output file
header("Content-type: $mimeType");
echo $contents;
}
else {
$dir = (isset($_GET['dir'])) ? $_GET['dir'] : '';
$conn = ftp_connect($ftpHost) or die("Could not connect");
ftp_login($conn, $ftpUser, $ftpPass);
// change dir to avoid ftp_rawlist bug for directory names with spaces in them
ftp_chdir($conn, "$startDir/$dir");
// fetch the raw list
$list = ftp_rawlist($conn, '');
foreach ($list as $item) {
if(!empty($item)) {
// Split raw result into pieces
$pieces = preg_split("/[\s]+/", $item, 9);
// Get item name
$name = $pieces[8];
// Skip parent and current dots
if ($name == '.' || $name == '..')
continue;
// Is directory
if ($pieces[0]{0} == 'd') {
echo "<a href='?dir={$dir}/{$name}'><strong>{$name}</strong></a><br />";
}
// Is file
else {
echo "<a href='?file={$dir}/{$name}'>{$name}</a><br />";
}
}
}
ftp_close($conn);
}
You'll need to add a function which checks which type of file we're handling.
If it's a directory show the regular link (the one you're using now) and if it's an image
show a link with the path of the image.
Because $value contains the name of the file you can use end(explode('.',$value)); to find the ext. of the file (php , jpg , gif , png).
Use another condition with the piece of information and you can identify if it's a picture or not.
In order to build the path of the image you'll need to use the $_GET['dir'] variable's value.
For instance:
<a href='<?=$_GET['dir']?>/Flower.gif'>Flower.gif</a>
I hope you got the idea.

Categories