I store the original image path(included file name) in db when upload an image.
For example:
img/uploaded/photo.jpg
Then I generate its thumbnail and store in below directory NOT in db.
/img/uploaded/thumbs/photo_thumb.jpg
And I have following function but no idea how to get the thumb which belong to the url in db.
//ie: $file is img/uploaded/photo.jpg
public function get_thumb($file)
{
//get the path without filename
$get_path = dirname($file) . "/thumbs/";
//result img/uploaded/thumbs/ (how can i get the photo_thumb.jpg) here?
return $get_path;
}
Edit
basename($file) to get filename from path but how to add _thumb.jpg?
Don't have much experience with PHP but using this as starting point, I get:
$pattern = '/^.*\/(.*)$/'; // match everything after the last slash and store it in $1
$replacement = '$1';
$filename = preg_replace($pattern, $replacement, $file);
$get_path = $file . '/thumbs/' . $filename;
As I said, not much experience with PHP, but this should do it...
A more easy way to do this, could be:
Find the last / in $file
Insert thumbs/ after it or replace it with /thumbs/
Find the last . in the edited $file
Insert _thumb after it
You can find the postitions of / and . with the strrchr function (documented here).
You can do this :
public function get_thumb($file) {
$infos = pathinfo($file);
$path = $infos['dirname'] . '/thumbs/' . $infos['filename'] . '_thumb.' . $infos['extension'];
return $path;
}
Usually, the way I do this is, is to store just the filename in the db. (assuming that they are all in the same directory).
Then query the database and get the filename, store it in:
$filename;
Then I just echo out something like
echo base_url('images/thumbs/' . $filename);
Hope this helps!
Related
I need assistance to more understand the concept so I can become a better developer. I want to learn how to refactor the code and erase all duplications.
What's the best practices for image uploads? Renaming them correctly?
I have a block of code that handles two attachments:
if( $request->hasFile('LFImage') ) {
$destination = public_path('app/lostFound/lostItems' . $lostFound->LFImage);
if( File::exists($destination) )
{
File::delete($destination);
}
$file = $request->file('LFImage');
$extension = $file->getClientOriginalExtension();
$filename = $lostFound->LFNumber . '-' . $lostFound->lostItem . '.' . $extension;
$file->move('app/lostFound/lostItems', $filename);
$lostFound->LFImage = $filename;
}
if( $request->hasFile('handoverStatement') ) {
$destination = public_path('app/lostFound/handoverStatements' . $lostFound->handoverStatement);
if( File::exists($destination) )
{
File::delete($destination);
}
$file = $request->file('handoverStatement');
$extension = $file->getClientOriginalExtension();
$filename = $lostFound->lostItem . '-' . $lostFound->LFNumber . '.' . $extension;
$file->move('app/lostFound/handoverStatements', $filename);
$lostFound->handoverStatement = $filename;
}
They're exactly the same except with the upload directory.
How can I make it as a one code block across the entire application with changeable file name and location depending on the form?
Some file names require random strings, how can I "Edit" the random string to the file that was uploaded?
Best practice when uploading and storing files in Laravel is using Storage.
It has all needed methods to work with files, you can save the file like this:
use Illuminate\Support\Facades\Storage;
Storage::put('images/', $request->file('LFImage'));
In the documentation provided above, you can find other examples like renaming and moving files
In order to access these files from web as well, you can use the command php artisan storage:link, which creates a symbolic link to storage folder in your public folder. After you create the symbolic link, you can generate URL to the file like this:
asset('storage/test.txt')
To avoid duplications, you can create a function in your controller to create a file. You will then just call this function with different files to keep the file creation code in one place.
you can simply write this
if ($request->hasFile('logo')) {
deleteImageFromDirectory(setting('logo'), "Settings");
$data['logo'] = uploadImageToDirectory( $request->logo , "Settings");
}
and define uploadImageToDirectory function in your helper functions or create a trait
function uploadImageToDirectory($imageFile, $directory = '' ){
$imageName = $imageFile->getClientOriginalName(); // Set Image name
$imageFile->storeAs("/Images/$directory", $imageName, 'public');
return $imageName;
}
I'm using the below function:
class Image {
static function url($id, $type = 'maps') {
$path = UPLOAD_DIRECTORY . '/' . $type . '/';
$files = glob($path . $id . '.*');
if (count($files)) {
$ext = substr($files[0], strpos($files[0], '.') - strlen($files[5]));
return SERVER_URL . 'img/' . $type . '/' . $id . $ext;
} else {
return '';
}
}
}
It works fine when hosted on WAMP but when using on Unix running on nginx it doesn't display the image correctly because of the file path. It seems to add the physical path of the files in the URL so it thinks the patch of the file is http://localhost/demo/img/maps/20/public_html/demo/img/maps/20.png
On WAMP it displays correctly ie the URL is http://localhost/demo/img/maps/20.png
These are the defined variables:
define('SERVER_URL', 'http://localhost/demo/');
define('UPLOAD_DIRECTORY', dirname(__FILE__) . '/img');
id=20;
Physical location of the images are in /public_html/demo/img/maps/
How can I fix this on a unix OS.
Managed to work out a solution (btw I'm not a programmer).
Basically on the UNIX server the directory where the images were located had a dot in the path whereas on wamp it didn't.
So what I had to do is use the strrpost instead of strpost - this finds the position of the last occurrence of a substring in a string instead of the first occurance.
I can't get the picture to display/show when viewing, although the files are already stored in the database (table 'menu') http://i.imgur.com/wo1w90H.png. Also when I upload the images all at once, their file name would change automatically. I don't know how and why this happens. I use array to upload multiple images.
if (isset($_POST["Submit"])) {
--some code here--
if (isset($_POST["id_list"])) {
// if id list available
foreach($_POST["id_list"] AS $id) {
--some code here--
/* Handle file upload */
if ($_FILES['upload']['error'][$id] == 'UPLOAD_ERR_OK') {
$path = "images/newmenu/";
$path_parts = pathinfo($_FILES["upload"]["name"][$id]);
$extension = $path_parts['extension'];
$picture = md5(uniqid()) . "." . $extension;
if (move_uploaded_file($_FILES['upload']['tmp_name'][$id], $path . "/" . $picture)) {
$update = " UPDATE menu
SET MenuPicture='$picture'
WHERE MenuID=$id";
$mysqli->query($update) or die(mysqli_error($mysqli));
}
}
}
}
}
}
Below is the form and yes it does include enctype="multipart/form-data"
<input type="file" multiple name="upload[' . $id . ']" value="' . $record["MenuPicture"] . '">
Filename changes because you are generating it this way
$picture = md5(uniqid()) . "." . $extension;
uniqid() is based on current time and hashing it will cause the filename to change everytime
When I upload the images all at once, their file name would change automatically
It was due to this:
$picture = md5(uniqid()) . "." . $extension;
// And later
move_uploaded_file($_FILES['upload']['tmp_name'][$id], $path . "/" . $picture)
Basically, you are moving your uploaded file to a new filename for your image file, which is generated using uniqid() and hashed with md5(), with the file extension appended at the end.
I can't get the picture to display/show when viewing
How are you trying to display the picture? Is it from web browser, or you go straight to the directory and open from there? What error(s) did you get, if any?
Actually, have you tried to go to the directory and see whether the file is created inside the images/newmenu/ directory?
Also, for the target upload directory, you might want to append it with $_SERVER['DOCUMENT_ROOT'] so that the target directory is not dependent on where your script is located, but it's always based on the root.
By the way, you might know already, but there is an entry in PHP manual page on uploading multiple files
I am uploading a file using Codeigniter's File Uploading Library and trying to insert the URL into the database. Codeigniter only supplies the server_path when using $this->upload->data(), which isn't usable for displaying the image to users.
I could normally just do something like base_url('uploads) . '/' . $data['file_name'] but I am storing the images in a folder for each post.
For example, I am getting C:/xampp/htdocs/site/uploads/32/image.jpg as the full_path, how can I convert this to http://mysite.com/uploads/32/image.jpg
The only thing that comes to mind is using a regular expression, but I feel like there has to be PHP function or Codeigniter function to help with this?
How can I convert the server path into the correct URL?
You can use $_SERVER['HTTP_HOST']; to get the url. Using the ID for your post, you can construct your path like so:
$url = "http://" . $_SERVER['HTTP_HOST'] . "/" . $id . "/" . basename($data['file_name']);
the basename() function returns everything after the last / in your path.
you only need to save the filename to the database and just use the post id and filename:
$url = "http://mysite.com/uploads/" . $postID . "/" . $fileName;
to get the file name use: How to get file name from full path with PHP?
<?php
$path = "C:/xampp/htdocs/site/uploads/32/image.jpg";
$file = basename($path); // $file is set to "image.jpg"
$file = basename($path, ".jpg"); // $file is set to "image"
?>
In CodeIgniter you should use $config['base_url']
You can use the ENVIRONMENT to setup different base_url, for example:
switch(ENVIRONMENT)
{
case 'development':
$config['base_url'] = 'http://localhost/';
break;
case 'testing':
$config['base_url'] = 'http://testing.server.com/';
break;
default:
$config['base_url'] = 'http://liveserver.com/';
}
See config.php
Now simply replace your local path with the base_url, str_replace should do the job (documentation here)
$newpath = str_replace("C:/xampp/htdocs/site/", $config['base_url'], $localpath);
Also, if you're interested in getting parts of the path, you could use explode (documented here) with / to create an array with every "section" of your path
$pathElements = explode('/', $localpath);
In this case, $pathElements[0] is C:, $pathElements[1] is xampp, etc...
I need help with adding the feature to check whether a file exists when uploading.
This is how the upload.php code looks like for uploading:
$file_name = $HTTP_POST_FILES['ljudfil']['name'];
$random_digit=rand(0000,9999);
$mp3 ='.mp3';
$pdf ='.pdf';
$datum = date('Ymd');
$new_file_name=$random_digit.$file_name;
$target_path1 = $target_path . $orgnr . '_' . $gsm . $pdf;
$target_path3 = $target_path . 'AC' . $datum . $new_file_name . $mp3;
$target_path11 = $target_path4 . $orgnr . '_' . $gsm . $pdf;
$target_path33 = $target_path4 . 'AC' . $datum . $new_file_name . $mp3;
$targetljudfilftp = 'AC' . $datum . $new_file_name . $mp3;
move_uploaded_file($_FILES['avtalsfil1']['tmp_name'], $target_path1);
move_uploaded_file($_FILES["ljudfil"]["tmp_name"], $target_path3);
$sql = "INSERT INTO affarer (tid, cid, orgnr, ljudfilftp) VALUES
(CURDATE(),'$date','$cid','$orgnr', '$targetljudfilftp')";
As you can see, it renames the uploaded file including a random number.
Sometimes, it happens that it renames the file to a number that already exists.
When that happens, it overwrites the previous file on my server.
So, how can I add a function to check whether the target name exists before it is used for renaming?
You can use
if (file_exists($target_path1))
to verify whether a file exists.
You would do better, though, to change strategy and employ tempnam:
$target_path = tempnam ($target_path, 'AC' . $datum . $file_name . $mp3)
This will create a file such as "AC_2012_Anacreon.mp3_xTfKxy" but you have the guarantee of it being unique, while even using file_exists would expose you to the risk of a concurrency collision.
Of course the file no longer has a .mp3 extension, so you have to take it into account when you scan the directory and supply files for download.
A still not secure, but maybe easier way is this:
for(;;)
{
$newname = // some strategy to generate newname, including a random
if (!file_exists($newname))
touch($newname);
if (!filesize($newname))
break;
}
or you can use a lock file to guarantee no concurrency (and therefore, that file_exists will return the truth and it will stay the truth):
$fp = fopen('.flock', 'r+');
if (flock($fp, LOCK_EX))
{
for(;;)
{
$newname = // some strategy to generate newname, including a random
if (!file_exists($newname))
{
// this creates the file uniquely for us.
// all other writers will find the file already there
touch($newname);
}
}
flock($fp, LOCK_UN);
}
else
die("Locking error");
fclose($fp);
// $newname is now useable.
Use the file_exists builtin function.
You could change the way your random digists are created by using (for example) a time based method
$random_digit = microtime(TRUE);
You can use as below
$random_digit = time();
Instead off using 'rand' function which can generate a duplicated number, you can use 'uniqid'php function, it returns a unique id ( http://www.php.net/manual/en/function.uniqid.php ).
If you still want to use the 'rand' you can use 'file_exists' function with the generated file name as param ( http://www.php.net/manual/en/function.file-exists.php ), but if a file exists you must regenerate the file name, so you will iterate each time the file exists.
At last, think to use full time date('Ymdhis') format instead off date('Ymd'), it's also better to use timestamp by calling time() function ( http://www.php.net/manual/en/function.time.php )
Anas,
if (file_exists($random_digit)) {
$random_digit = rand(0000,9999);
}