Jump to content

Add Image from input type="file" possible - or must it be WireUpload?


simonGG
 Share

Recommended Posts

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

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.

 

  • Like 1
Link to comment
Share on other sites

@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.

  • Like 1
Link to comment
Share on other sites

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;
}

 

  • Like 3
Link to comment
Share on other sites

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.

  • Like 3
Link to comment
Share on other sites

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

  • Like 1
Link to comment
Share on other sites

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;
}

 

  • Like 1
Link to comment
Share on other sites

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
}

 

  • Like 1
Link to comment
Share on other sites

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

  • Like 2
Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...