I am trying to make image upload on laravel but at some point it fails to set image destination in database.
When i try and test and echo back final image and link destination everything works fine, when i check the image destination and on my destination folder image is there but only image wont set in database.
I have set this field in database
Name Type Collation Attributes Null Default
avatarfull varchar(500) utf8_unicode_ci No None
i increased varchar to 500 just in case.
and on my usercontroller i have this for storing new user
public function store() {
$validator = Validator::make(Input::all(), [
'photo' => 'mimes:jpeg,bmp,png|max:1000'
]);
if($validator->fails()) {
return Redirect::route('admin.users.create')->withErrors($validator)->withInput();
} else {
if(Input::hasFile('photo')) {
if(Input::file('photo')->isValid()) {
$imagename = str_random(20) . '.' . Input::file('photo')->getClientOriginalExtension();
$imagelocation = Input::file('photo')->move(base_path() . '/img', $imagename );
$avatarfull = 'img/' . $imagename;
} else {
$avatarfull = 'img/profile_default.png';
}
} else {
$avatarfull = 'img/profile_default.png';
}
$create = User::create([
'avatarfull' => $avatarfull,
]);
if($create) {
return Redirect::route('admin.users.index')
->with('success_message', 'New user is successfully created!');
}
}
}
So the validator checks for what it needs to check, if the image is that mime type, and checks if image size doesn't exceed 1mb, that works.
Further on input checks if hasfile set for upload, if file is set for upload, it checks if file is valid, and than it gets image, rename it to random string, copy to proper location and set $avatarfull variable to proper path for setting it in the database.
If i
echo $avatarfull
or
print_r($avatarfull)
or
var_dump(Input::all())
I get all the data correct as it should be
var_dump shows all the data of input fields and there are laravel's usual backend things like checking the file type, file size, storing it in temp path, moved path to new location. Everything works fine.
if i check at the end $avatarfull variable, just before i store a new user $avatarfull variable is as it should, if image is uploaded than it echoes
img/random10characterstring.jpg
Checking against base path image is properly stored and moved to wanted location on my pc
/opt/lampp/htdocs/laravel/img/random10characterstring.jpg
Using in combination to link_to it shows the final results of url linked image
localhost/laravel/img/random10characterstring.jpg
And of course checking in my file browser the image does exists in that path. So laravel done it's work uploaded image, renamed it, and moved to desired location.
Only it won't set the field in the databas. When i add new user (this is image field for user avatar) it adds new user in the database, and i get set all the fields in the database except i don't get set path to image in database. For some reason this field lefts blank.
Please check if avatarfull is in $fillable array in your User model. If not Users model mass assignment protection will not allow to store the data by Users::create(...).
If you don't want to put avatarfull in fillable array, you can always use:
$user = new User;
$user->avatarfull = $avatarfull;
$user->save();
For more information about mass assignment see: http://laravel.com/docs/4.2/eloquent#mass-assignment
Related
So I've mistakenly updated all of the database contents to a single image file (junior mistakes, luckily there's a backup). I'm now trying to update the f'd-up database using the restored database's image column. However upon pulling, it gets saved as a string rather than its original image type. Is there a way to convert it back to image?
$main = DB::connection('restoredDB')
->table('Returns')
->select('signature', 'returnID')
->orderBy('lastUpdate', 'desc')
->chunk(300, function($items) {
foreach($items as $item)
{
dd(gettype($item->signature)); //stopped here to determine data type
DB::connection('destroyed_db')
->table('Returns')
->where('returnID', $item->returnID)
->update([
'signature' => $item->signature
]);
}
});
So I need users to upload files. These files can then be bought by customers (who are not users). Once the payment has been processed by stripe, the buyer should receive an email with a link to download the file.What I need is a download link for these files, that is only available to the user who has uploaded the file, and the customers who have bought the file.
Here is my controller for processing a form filled by a user to upload a file. NonUser is the details for the file from a previous form.
$note = new Note;
$note->title = $nonUser->title;
$note->description = $nonUser->description;
$note->mark = $nonUser->mark;
$note->page_count = $nonUser->page_count;
$note->subject_id = $nonUser->subject_id;
$note->year = $nonUser->year;
$note->modules = $nonUser->modules;
$note->price = $nonUser->modules*25;
$note->user_id = $user->id;
$note->exam_id=$user->exam_id;
$note->save();
Storage::put(
'notes/' . $note->id . '.pdf',
file_get_contents($request->file('notes')->getRealPath())
);
My stripe processing.
public function charge()
{
$nid = Session::get('nid');
$note = Note::where('id','=',$nid)->first();
$price = $note->price;
\Stripe\Stripe::setApiKey("sk_test_key");
$token = $_POST['stripeToken'];
try {
$charge = \Stripe\Charge::create(array(
"amount" => $price*100, // amount in cents, again
"currency" => "aud",
"source" => $token,
"description" => "Example charge"
));
} catch(\Stripe\Error\Card $e) {
flashWarning('An error occured');
return back();
}
flash('payment succesful! Check your email for a download link!');
return back();
}
I would use the following steps to service buyers:
Once payment is successful, store in the DB the orderID, fileID (should match the primary key of all files, stored in a different table) a random hash as download_ticket,a DateTime as ticket_expiration, and the # of times the ticket was used as download_count
Email the buyer a download link that points to a php script. The script should expect the download ticket. example:
example.com/download.php?ticket=m54hm4j390534gi2frew0094
In the script download.php, you would do the following:
Grab ticket from the query string: $ticket = $_GET['ticket']
Get the record in the DB: SELECT * from tbl WHERE ticket=m54hm4j390534gi2frew0094
If there is no match, error 404 not found with http_response_code(404) and abort.
If ticket_expiration has passed, delete the record, error 403 forbidden and abort.
If download_count has exceeded a limit, delete the record, error 429 too many requests and abort.
Use the fileID column to find the file that was purchased
If all checks workout, you can send the file to the user. do not redirect the user to the file's true location. Instead, do something like:
$path = '...'; // real path on disk
header('Content-Type: application/pdf');
header('Content-Length: ' . filesize($path));
$pipe = fopen($path, 'rb');
fpassthru($pipe); //sends file to user
fclose($pipe);
//TODO: increment `download_count` in the purchase record
You can do this by the following way
Step 1 : Generate the Link
After you do
Storage::put(
'notes/' . $note->id . '.pdf',
file_get_contents($request->file('notes')->getRealPath())
);
Generate the link with
$data['link'] = URL::to('/notes/'.$note->id.'pdf'); //Modify path according to your need
$message = 'Your Custom HTML';
Step 2 : Send the Email
Trigger mail to the User who uploads it
Mail::send(email.file, function ($message) {
$message->from('youremail#example.com', 'File Received');
$message->to(Auth::user()->email); // Email id of one who uploads it
});
Trigger mail who bought the file
$userlist = User::where('file_id' , $fileid)->get(); //modify the condition according to the tables you have
foreach($userlist as $users)
{
Mail::send(email.file, function ($message) {
$message->from('youremail#example.com', 'File Received');
$message->to($user['email']); // Looped Email id
});
}
Additional Step : (To make sure the file is secure)
Don't Point the file directly, you can do like this
Generate the link like this
yourapp.com/pdf/12/143
Where 12 is the user id and 143 is the pdf file's id
And under your controller you can check whether the user with id 12 is authorized to download the file with id 143, If so, then generate the pdf view or download it to the user.
Hope this helps you
When the payment is processed successfully store the PK of the file upload (from your database) in a new table (let's call it purchased downloads) along with a unique token that can be used to look up the file's PK in that table. This will be the token you'd send along with the email for them to download the file.
Create a new controller that accepts this token and looks up the unique token in the purchased downloads table then you can use something like an X-Sendfile header to have your webserver serve up the file to the client from your file system if the token verifies. You can also set an expiration time on this token if you'd like.
I'm saving an image to uploads folder, but I'm using file_put_contents instead of wp_handle_upload - because I get an image in base64 and not as a file in $_FILES.
Image and certain post data are saved/updated as they should be using this functions:
wp_insert_attachment
wp_update_attachment_metadata
The problem is when I want to remove an old image (when saving a new one).
wp_delete_attachment does not remove the image (it does seem to remove stuff in db though..). I am thinking the problem lies in not using wp_handle_upload. (when I upload an image via upload btn and receive it using $_FILES and then upload it with wp_handle_upload - removing works)
Does anyone have an idea of what might be the right way to remove an image in my case?
Perhaps I can save it properly using wp_handle_upload even if I have an image in base64?
Thanks for any info.
EDIT: I also tried saving image with wp_upload_bits and wp_delete_attachment still did not work.
Another thing I checked: the code of wp_handle_upload function located in wp-admin/includes/file.php: I don't see an easy way to modify or copy the existing function and add a custom one which would accept base64 image instead of a file as in $_FILES. Someone has a "base64 to $_FILES" workaround perhaps?
as #KamilP said, WP inserts records in wp_posts and wp_postmeta tables.
So first you have to save your base64 images in temporary directory, then by using it's relative path and other data you can insert record in database by using wp_insert_attachment, link contains appropriate example.
This function will add image to media library.
To generate more thumbnails you can use wp_generate_attachment_metadata function. This function will update wp_postmeta table also with all details of image and thumbnails.
After this you can use wp_delete_attachment function to delete image from directory and database as well.
Another solution:
Use function from this link to generate image from base64 string.
then get mime type, size and path of image
create an array similar to $_FILES's.
then pass it to wp_handle_upload
UPDATE :
$_FILES array's structure is like this
array(5) {
'name' => string(8) "file name.extension" // file name with extension
'type' => string(0) "" // mime type of file, i.e. image/png
'tmp_name' => string(0) "" // absolute path of file on disk.
'error' => int(2) // 0 for no error
'size' => int(0) // size in bytes
}
You can create array like above with all details, use various file handling PHP functions to get size and mime type. Name is whatever you want to put, and tmp_name is a path of file on server where file is exists, in your case location of folder where you save your file from base64 string.
See updated link above for function which will give you image from base64 string.
I actually accomplished this before I saw an answer here, so I'm providing a solution here if someone runs into the same problem. I used the function below (first one) and also using wp_delete_attachment after just to be sure all stuff got removed.
/**
* Attempt at removing all images in uploads folder by providing an image url
* Example:
* - Provide http://mypage.com/wp-content/themes/mytheme/uploads/2015/12/testImage.jpg
* - this should remove all its images created when uploading:
* - testImage-150x150.jpg, testImage-300x300.jpg etc and also the provided original image
*
* We'r doing this because wp_delete_attachment() will not remove an image that was uploaded via a
* custom mytheme_upload_image() function (which is providing base64 image instead of file in $_FILES)
*
* TODO TODO mytheme_get_image_sizes() does not return ALL IMAGES THAT WERE CREATED (all sizes)
*/
function mytheme_remove_all_image_sizes_from_uploads($primary_image_url) {
$pi = pathinfo($primary_image_url);
$img_dirname = $pi['dirname'];
$img_dirname_exploded = explode('/',$img_dirname);
$last_dir = array_pop($img_dirname_exploded); // month usually (two digits)
$second_last_dir = array_pop($img_dirname_exploded); // year usually (4 digits)
$basename = $pi['basename']; // without trailing /
$img_name = $pi['filename'];
$img_extension = $pi['extension'];
$uploads = wp_upload_dir();
$base_uploads_dir = $uploads['basedir']; // without trailing /
$path_to_appropriate_uploads_dir = $base_uploads_dir.'/'.$second_last_dir.'/'.$last_dir.'/';
$img_name_to_remove = $img_name.'.'.$img_extension; // UNLINK
if(!#unlink($path_to_appropriate_uploads_dir.$img_name_to_remove)) {
// this image was not removed
}
$image_sizes = mytheme_get_image_sizes();
foreach($image_sizes as $size) {
$img_name_to_remove = $img_name.'-'.$size.'.'.$img_extension; // UNLINK
$img_path = $path_to_appropriate_uploads_dir.$img_name_to_remove;
if(mytheme_image_on_url_exists($img_path) && !#unlink($img_path)) {
// this image was not removed
}
}
}
/**
* Get size information for all currently-registered image sizes.
* Found an example of this on one of the wordpress' example pages ..
*
* #global $_wp_additional_image_sizes
* #uses get_intermediate_image_sizes()
* #return array $sizes Data for all currently-registered image sizes.
* TODO TODO mytheme_get_image_sizes() does not return ALL IMAGES THAT WERE CREATED (all sizes)
*/
function mytheme_get_image_sizes() {
global $_wp_additional_image_sizes;
$sizes = array();
foreach ( get_intermediate_image_sizes() as $_size ) {
if ( in_array( $_size, array( 'thumbnail', 'medium', 'large' ) ) ) {
$width = get_option( "{$_size}_size_w" );
$height = get_option( "{$_size}_size_h" );
} elseif ( isset( $_wp_additional_image_sizes[ $_size ] ) ) {
$width = $_wp_additional_image_sizes[ $_size ]['width'];
$height = $_wp_additional_image_sizes[ $_size ]['height'];
}
$img_name_end = $width."x".$height;
if(!in_array($img_name_end,$sizes)) {
$sizes[] = $img_name_end;
}
}
// ADD CUSTOM SIZES (this one is not returned otherwise?!)
$sizes[] = '300x200';
return $sizes;
}
The simple solution is probably to just manually add _wp_attached_file meta value to your attachment.
Set it to the file you want deleted when the attachment is deleted.
The logic behind this is according to https://core.trac.wordpress.org/browser/tags/4.4/src/wp-includes/post.php#L0:
get_attached_file returns that value to wp_delete_attachment which sets it as $file
And at the end of wp_delete_attachment it calls wp_delete_file($file)
So I'm guessing the underlying issue is that, you are never setting a value for the meta key _wp_attached_file.
The only downside is that I'm not sure what other processes in wp might be using that value. But you might get lucky, perhaps none, and/or it doesn't mess anything up by setting it manually.
hi i've got a gallery page. this gallery page has a gallery image object with an has_many relation.
private static $has_many = array(
'GalleryImages' => 'GalleryObject'
);
my gallery object has an image upload field. I want to set the upload folder to the title of the gallery page
i tried this with no result
$visual->setFolderName('Galerie/'.$this->Gallery()->Title);
and this (what i would prefer)
public function getGalleryTitle() {
$galleryTitle = $this->Gallery()->Title->First();
$uploadFolder = str_replace(' ', '-', $this->$galleryTitle);
return $uploadFolder;
}
$visual->setFolderName('Galerie/'.$this->$uploadFolder);
the second returns and error (undefined variable uploadFolder ?!) and my upload folder is now set to "Galerie/DataList"
can someone tell me how to convert the output of $uploadFolder so that i get back the title?
EDIT:
GalleryHolder: http://www.sspaste.com/paste/show/5267dea3579a6
GalleryPage: http://www.sspaste.com/paste/show/5267dee4c9752
GalleryObject: http://www.sspaste.com/paste/show/5267df0af1a65
you where almost there..
Here is your edited getGalleryTitle() function.
It is basically checking if the GalleryObject has a parent Gallery via $this->GalleryID. Since it is a has_one relation the column will be named GalleryID.
Then we get the Gallery object with $this->Gallery() and get it's title with $gallery->Title.
I've also replaced your str_replace with SilverStripe's URLSegmentFilter class. Which will removed spaces and other special characters non welcome in URL, a better solution.
public function getGalleryTitle()
{
if ( $this->GalleryID )
{
$gallery = $this->Gallery();
$filter = new URLSegmentFilter();
return $filter->filter( $gallery->Title );
}
else{
return 'default';
}
}
Then in the getCMSFields() function, when creating your UploadField we just call the getGalleryTitle() function that returns the string for the folder name.
$visual = new UploadField('Visual', _t('Dict.IMAGE', 'Image'));
$visual->setFolderName('Galerie/'.$this->getGalleryTitle());
A few notes..
$this references the current Object instance, so you can't use $this->$galleryTitle to access a variable you just created in your function, $galleryTitle by itself is enough.
You were calling $this->$uploadFolder in setFolderName, this doesn't work for the same reason, and also, using $uploadFolder by itself wouldn't work since this variable was created in the scope of another function. So we just call the function we defined on our Object with $this->getGalleryTitle() since it returns the value we want.
This should work fine, but keep in mind that if the Title of the Gallery changes at some point, the folder name will change too. So you might end up with images uploaded in many different folders for the same gallery... I personally wouldn't advise it, unless you implement some kind of "Title locking system" or some way to keep the "correct" or first "valid/acceptable" Gallery title in a separate object property that can't be edited and use this in the folder name.
I usually only use the ID in those case ($gallery->ID), as this will not change.
edit
Another version of getGalleryTitle() that should work even if the GalleryObject isn't saved yet.
public function getGalleryTitle()
{
$parentID = Session::get('CMSMain')['currentPage'];
if ( $parentID )
{
$gallery = Page::get()->byID( $parentID );
$filter = new URLSegmentFilter();
return $filter->filter( $gallery->Title );
}
else{
return 'default';
}
}
First, I check to see whether we're on the CMSSettingsPage or in a ModelAdmin page (Should you be using them). You want to get all the information about which class the controller is managing as it's data record. (If you have firebug, FB($this) in getCMSFields() on the related DataObject (DO) will show you the page managed under DataRecord)
Controller::curr()->currentPage() will get you the current page the DO is being managed on, and ->URLSegment will get the page url name, though you could use Title or MenuTitle also.
Here is an example which will set up a folder underneath assets/Headers to save images in. Running this on the HomePage (ie URL Segment 'home') will create and save objects into the folder /assets/Headers/home.
if (Controller::curr()->class == 'CMSSettingsController' || Controller::curr() instanceof Modeladmin) {
$uploadField->setFolderName('Headers');
}
else
{
$uploadField->setFolderName('Headers/' . Controller::curr()->currentPage()->URLSegment);
}
Does anyone know how I can get the just uploaded images ids and access the array of them from my controller?
My model: http://pastebin.com/aJW0vq22 (do_upload())
And here's the relevant part of my controller:
class Site extends CI_Controller {
function index() {
//enable profiler
//$this->output->enable_profiler(TRUE);
$data = array();
$this->load->model('Site_model');
if($this->input->post('upload')) {
$data['upload'] = $this->Site_model->do_upload();
//echo '<hr />' . $this->Site_model->total_uploads;
//set the users edit session for their image
$uploaded_image_id = $this->Site_model->get_last();
$values = array(
'image_id' => $uploaded_image_id,
'session_id' => $this->session->set_userdata('session_id')
);
$this->session->set_userdata('edit', $values);
//var_dump($values);
//show uploaded image
redirect($uploaded_image_id . '?links');
}
Currently I've just been using 'get_last()' to just get the last thing added to the database, but now I've added the ability to upload multiple things at once I doubt I can still, any ideas?
edit:
basically the end result i'm trying to get is
redirect('id1, id2, id3' . '?links');
You could save the file names of the just uploaded images into a session array and then query your database for the ids of those file names. This will reliably return the desierd ids.
I see that for your get_last you use a query that returns the last image added to the table. But what if you have 500 users uploading an image at the same time?