simonGG Posted August 25, 2016 Share Posted August 25, 2016 Hi there, im trying to add images to a page. The data is send via ajax to the php. The php gets the local image urls. They look like: data:image/png;base64,iVBORw0KGgo... Now i try to write the images to to page like this: foreach ($item_images as $image) { $p->images->add($image); } Where $item_images is a array send via js with the local image urls. This causes a Error: <b>Fatal error</b>: Call to a member function add() on null in ... So im unsure if images->add expects another data format than this local image url (see above). Or im doing somthing wrong or this is totaly wrong and i have to use WireUpload?. Full php: <?php require_once '../index.php'; $item_titel = $_GET["item_titel"]; $item_client = $_GET["item_client"]; $item_date = $_GET["item_date"]; $item_usecase = $_GET["item_usecase"]; $item_images = $_GET["item_images"]; $item_text = $_GET["item_text"]; $targetPage = "/items/"; $p = new Page(); $p->template = 'item'; $p->parent = wire('pages')->get($targetPage); $p->title = $item_titel; $p->item_titel = $item_titel; $p->item_client = $item_client; $p->item_date = $item_date; $p->of(true); foreach ($item_images as $image){ $p->images->add($image); // $image looks like: data:image/png;base64,iVBORw0KGgo... } $p->item_text = $item_text; $p->save(); $p->of(false); Any help is very much appreciated .) cheers Simon Link to comment Share on other sites More sharing options...
BitPoet Posted August 25, 2016 Share Posted August 25, 2016 Try save()ing $p before you assign the images. Some fields, and image/file fields in particular, only get initialized once the page is saved for the first time. I'm not sure if passing a data url to Pageimages::add will work though. If you hit an error there, you may have to extract the raw image data from $image using file_get_contents(), write those to temporary image file and pass the path to the temporary file to the method. 1 Link to comment Share on other sites More sharing options...
netcarver Posted August 25, 2016 Share Posted August 25, 2016 @simonGG I think Bitpoet is on the right tracks here regarding the need to save the page before adding images. The page does need to be saved before you attempt to add images to it as saving creates the assets directory that the images will eventually be copied into when they are added. IIRC, adding an image, eventually calls PHP's copy() function (look at the changelog section) which does allow adding images via URLs since PHP 4.3.0 provided that fopen wrappers are enabled. I have successfully added remote images to local PW pages before using the URLs of the remote images. However, not saving the page first is not your only issue. Firstly, does the item template have a field called "images"? If not, it needs one. Second - and I'm slightly unsure about this - It looks like you are actually passing in the actual base64 encoded images rather than an array of URLs to the images. Your code expects this line... $item_images = $_GET["item_images"]; ...to give you an array of URLs that you can iterate over - but it seems to be giving you an array of strings that look like the base64 encoded content of the URL (guessing as I can't tell what you are doing here.) I think you will need your $item_images variable to be an array of URLs like https://path/to/some/image.png for this method to work. Sorry I can't be more specific. 1 Link to comment Share on other sites More sharing options...
horst Posted August 25, 2016 Share Posted August 25, 2016 Like Bitpoet and netcarver said, you need to save the page before you are able to add images. ALso you should do a check if the page already exists, maybe. If you are not able to provide (HTTP) URLs to the images, and no filepathes, but those strings of data:image/png strings, you need two things: a wrapper function that extracts the image data into a temporary file and an imagename! A wrapper function is easy, but from where do you get a filename, ...? <?php require_once('../index.php'); $input = wire('input'); $sanitizer = wire('sanitizer'); $pages = wire('pages'); $item_titel = $sanitizer->text($input->get("item_titel")); $item_client = $sanitizer->text($input->get("item_client")); $item_usecase = $sanitizer->text($input->get("item_usecase")); $item_text = $sanitizer->textarea($input->get("item_text")); $item_date = $input->get("item_date"); $item_images = $_GET["item_images"]; // don't know if we can use $input->get("item_images") to get an array, // writing in browser only(!) $parentPage = $pages->get("/items/"); $templatePage = wire('templates')->get("item"); $pageName = $sanitizer->pageName($item_titel); $p = $pages->get("name={$pageName}, parent={$parentPage}, template={$templatePage}"); if(0 == $p->id) { // or: if($p InstanceOf NullPage) // only create a new page if no one exists, // otherwise you would create pages with same content but automaticly incremented pagename !! $p = new Page(); $p->of(false); $p->template = $templatePage; $p->parent = $parentPage; $p->title = $item_titel; $p->item_titel = $item_titel; $p->item_client = $item_client; $p->item_date = $item_date; $p->item_text = $item_text; $p->save(); foreach($item_images as $data) { // $data looks like: data:image/png;base64,iVBORw0KGgo... $basename = '???'; $filename = myImageExtract($data, $basename); $p->images->add($filename); } $p->save(); } function myImageExtract($data, $basename = '') { // extract the imagetype / extension and the imagedata part from the string $imagedata = ... ; $extension = ... ; // write it into a temporary file and return the filename // see: https://processwire.com/talk/topic/11843-file-upload-images-pdfs-from-base64-value/#comment-110113 $tmpDir = new WireTempDir('someName'); $fileBasename = $basename . '.' . $extension'; $filename = $tmpDir->get() . $fileBasename; file_put_contents($filename, base64_decode($imagedata)); return $filename; } 3 Link to comment Share on other sites More sharing options...
BitPoet Posted August 25, 2016 Share Posted August 25, 2016 In addition to hort's suggestion, writing the raw image data contained in the data URI can be as simple as: file_put_contents($filename, file_get_contents($data)); No need for manually parsing $data for the base64 part. The only reason to look at $data would be to determine the correct file extension in case other types than png are allowed too. 3 Link to comment Share on other sites More sharing options...
simonGG Posted August 25, 2016 Author Share Posted August 25, 2016 Hey thank you all for your input and the effort you put in this. I am working on it and will let you know how it goes! Thanks again! Cheers Simon Link to comment Share on other sites More sharing options...
simonGG Posted August 25, 2016 Author Share Posted August 25, 2016 So finaly it works -) What do you think - is it okay this way or is there something that should be fixed? PS: the $tmpDir solution didn't work - somehow the images couldn't be found... $tmpDir = new WireTempDir('someName'); Actual code: <?php require_once('../index.php'); $input = wire('input'); $sanitizer = wire('sanitizer'); $pages = wire('pages'); $item_titel = $sanitizer->text($input->get("item_titel")); $item_client = $sanitizer->text($input->get("item_client")); $item_usecase = $sanitizer->text($input->get("item_usecase")); $item_text = $sanitizer->textarea($input->get("item_text")); $item_date = $input->get("item_date"); $image_fileNames = $_GET["image_fileNames"]; $item_images = $_GET["item_images"]; $parentPage = $pages->get("/items/"); $templatePage = wire('templates')->get("item"); $pageName = $sanitizer->pageName($item_titel); $upload_path = $config->paths->assets; $p = $pages->get("name={$pageName}, parent={$parentPage}, template={$templatePage}"); if(0 == $p->id) { $p = new Page(); $p->of(false); $p->template = $templatePage; $p->parent = $parentPage; $p->title = $item_titel; $p->item_titel = $item_titel; $p->item_client = $item_client; $p->item_date = $item_date; $p->item_text = $item_text; $p->save(); $i = 0; foreach($item_images as $data) { $name = $image_fileNames[$i]; // fortunately i can pass this from ajax $filename = myImageExtract($data, $name, $upload_path); $p->item_images->add($filename); $i++; } $p->save(); } function myImageExtract($imgData, $imgName, $assets) { $base64img = str_replace('data:image/png;base64,', '', $imgData); // TODO - must be dynamic - could be jpg, gif ... $data = base64_decode($base64img); $file = $assets . $imgName; file_put_contents($file, $data); return $file; } And thanks again - this realy a helpfull forum! Cheeers Simon 1 Link to comment Share on other sites More sharing options...
BitPoet Posted August 25, 2016 Share Posted August 25, 2016 I'd pick a different upload directory that $config->paths->assets (which amounts to site/assets). Perhaps create an upload directory inside the assets dir and save it there. You could also shorten your image extraction function a little (and make it image type agnostic): function myImageExtract($imgData, $imgName, $assets) { $file = $assets . $imgName; file_put_contents($file, file_get_contents($imgData)); return $file; } 1 Link to comment Share on other sites More sharing options...
horst Posted August 25, 2016 Share Posted August 25, 2016 Hi, looks good. Only additional suggestion from me would be to use another upload path ($config->uploadTmpDir) if is set in your environment, but not really necessary. And you better should remove the temporary file after it got added to the image field. ... $i = 0; foreach($item_images as $data) { $name = $image_fileNames[$i++]; // fortunately i can pass this from ajax myImageExtract($p, $data, $name, $upload_path); } $p->save(); } function myImageExtract(&$p, $imgData, $imgName, $uploadPath) { $base64img = str_replace('data:image/png;base64,', '', $imgData); // TODO - must be dynamic - could be jpg, gif ... $data = base64_decode($base64img); $file = $uploadPath . $imgName; file_put_contents($file, $data); $p->item_images->add($file); unlink($file); // delete temp file after adding it to image field } 1 Link to comment Share on other sites More sharing options...
simonGG Posted August 25, 2016 Author Share Posted August 25, 2016 Hi Horst, removing the temporary files is a good idea .) - i diden't recognized that they flooded my asset folder - thanks! Just if someone is interested: I had to change from GET to POST because with GET i struggled into a HTTP 414 “Request URI too long” ERROR.So finnaly it works how i wanted - thanks to the team .) - and a good evening to you. <?php require_once('../index.php'); $input = wire('input'); $sanitizer = wire('sanitizer'); $pages = wire('pages'); $item_titel = $sanitizer->text($input->post("item_titel")); $item_client = $sanitizer->text($input->post("item_client")); $item_usecase = $sanitizer->text($input->post("item_usecase")); $item_text = $sanitizer->textarea($input->post("item_text")); $item_date = $input->post("item_date"); $image_fileNames = $_POST["image_fileNames"]; $item_images = $_POST["item_images"]; $image_types = $_POST["image_types"]; $parentPage = $pages->get("/items/"); $templatePage = wire('templates')->get("item"); $pageName = $sanitizer->pageName($item_titel); $upload_path = $config->paths->assets; $p = $pages->get("name={$pageName}, parent={$parentPage}, template={$templatePage}"); if(0 == $p->id) { $p = new Page(); $p->of(false); $p->template = $templatePage; $p->parent = $parentPage; $p->title = $item_titel; $p->item_titel = $item_titel; $p->item_client = $item_client; $p->item_date = $item_date; $p->item_text = $item_text; $p->save(); $i = 0; foreach($item_images as $data) { $name = $image_fileNames[$i]; $imgType = $image_types[$i]; myImageExtract($p, $data, $name, $upload_path, $imgType); $i++; } $p->save(); } function myImageExtract($p, $imgData, $imgName, $upload_path, $imgType) { $base64img = str_replace('data:'.$imgType.';base64,', '', $imgData); // fixed $data = base64_decode($base64img); $file = $upload_path . $imgName; file_put_contents($file, $data); $p->item_images->add($file); unlink($file); } Cheers Simon 2 Link to comment Share on other sites More sharing options...
netcarver Posted August 25, 2016 Share Posted August 25, 2016 Slight tweak - probably never be needed... if (false !== file_put_contents($file, $data)) { $p->item_images->add($file); unlink($file); } Might save you an error if a file save ever fails. 2 Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now