Posted by lightguardjp on November 23, 2015 at 6:14pm
I'm trying to figure out the best way to handle images in a headless drupal 8 install. Using the /entity rest endpoint is not working during serialization as it seems like I need to have a URI for the existing file on the drupal install. I'd like to be able to do everything in one step, if that's possible.
Comments
When I did this in 7 for the
When I did this in 7 for the back-end of http://www.nickjr.co.uk/create/#!/colour I had to create a custom call due to the way Drupal works uploading images first then providing that URI for the node. I just copied the code from the original functions (apart from the random filename generator which I copied from elsewhere) - I've cleaned out the project-specific stuff (it links with the line-art etc.) & pasted below so you can see how it did it, again it's not for 8 & not tried it but AFAIK you'll probably need to do similar - hope it helps!
/**
* POST a new image
@param int $uid
* Integer specifying the user id
* @param object $image
* A base64 encoded image
* @param string $title
* String specifying the title of the image
@return array
* An associative array containing the nid of the node created
*/
function _mysite_api_image_create($uid, $title = NULL, $image) {
// Ensure the user account exists
$account = user_load($uid);
if (empty($account)) {
return services_error(t('Error during POST/image: There is no user with uid @uid.', array('@uid' => $uid)), 404);
}
// Adds backwards compatability with regression fixed in #1083242
// $image['image'] can be base64 encoded file so we check whether it is
// file array or file data.
$image = _services_arg_value($image, 'image');
// Create a random filename
srand((double) microtime() * 1000000);
$filename = '';
while (strlen($filename) != 20) {
$type = rand(1, 3);
if ($type == 1) {
$filename = $filename . chr(rand(48, 57));
}
if ($type == 2) {
$filename = $filename . chr(rand(65, 90));
}
if ($type == 3) {
$filename = $filename . chr(rand(97, 122));
}
}
// Title
if ((isset($title) && ($title != ''))) {
$title = check_plain($title);
}
else {
$title = $filename;
}
// If the file data or filename is empty then bail.
if (!isset($image['image'])) {
return services_error(t("Error during POST/image: Missing data the file upload can not be completed"), 500);
}
// As requested image sent in base64 so need to extract file type
// Image data such as file type could be sent along as other fields in file array
$imgdata = base64_decode($image['image']);
$f = finfo_open();
$mime_type = finfo_buffer($f, $imgdata, FILEINFO_MIME_TYPE);
$extensions = array(
'image/gif' => '.gif',
'image/jpeg' => '.jpg',
'image/png' => '.png',
);
$image['filename'] = $filename . $extensions[$mime_type];
// Get the directory name for the location of the file:
$image['filepath'] = file_default_scheme() . '://field/image/' . $uid . "/" . $image['filename'];
$dir = drupal_dirname($image['filepath']);
// Build the destination folder tree if it doesn't already exists.
if (!file_prepare_directory($dir, FILE_CREATE_DIRECTORY)) {
return services_error(t("Error during POST/image: Could not create destination directory for file."), 500);
}
// Rename potentially executable files, to help prevent exploits.
if (preg_match('/.(php|pl|py|cgi|asp|js)$/i', $image['filename']) && (drupal_substr($image['filename'], -4) != '.txt')) {
$image['filepath'] .= '.txt';
$image['filename'] .= '.txt';
}
// Write the file
if (!$image_saved = file_save_data(base64_decode($image['image']), $image['filepath'])) {
return services_error(t("Error during POST/image: Could not write file to destination"), 500);
}
if (isset($image['status']) && $image['status'] == 0) {
// Save as temporary file.
$image_saved->status = 0;
file_save($image_saved);
}
else {
// Required to be able to reference this file.
file_usage_add($image_saved, 'services', 'files', $image_saved->fid);
}
// Create a new node for this image
$node = new stdClass();
$node->title = $title;
$node->language = LANGUAGE_NONE;
$node->type = "image";
node_object_prepare($node);
$node->name = $account->name;
$node->status = 1; //(1 or 0): published or not
$node->promote = 0; //(1 or 0): promoted to front page
$node->comment = 0; // 0 = comments disabled, 1 = read only, 2 = read/write
$node->field_image[$node->language][]['fid'] = $image_saved->fid;
$node = node_submit($node); // Prepare node for saving
try {
node_save($node);
}
catch (Exception $e) {
return services_error(t("Error during POST/image: Error saving node."), 500);
}
return array("nid" => $node->nid);
}
Thanks
I figured I'd need to something like that :(
Yeah, thought I'd kill the
Yeah, thought I'd kill the dream quick :D
/should/ be easier in 8, and perhaps something which could come back as a useful addition/module, I'd be up for reviewing/tidying up/helping out, although it might be something that turns out instance-specific & example solution like I posted above may be the furthest we can get otherwise I would've thought someone would have done similar already - maybe they do but just move on!
Do post back results if you can, thanks!
Could I hook this into the existing chain?
Any ideas if I could hook this into the existing entity chain? Can a module override a base type?
So I found a D7 module which
So I found a D7 module which does this - came out in March this year so about a year after I did the code above ;) Interesting to see how it approaches the issue, don't see any 8 version but seems like a good codebase to start with:
RESTful web services support for files and images:
https://www.drupal.org/project/restws_file
I ran into this with my
I ran into this with my application running AngularJS front-end and Drupal 8 back-end.
You do have to create the file before you can set your node field value to it. So basically I do a POST to create the file and then update the node image field.
I was researching to see how other people had solved this, I mostly found unanswered issues and a open thread with several in-progress patches attached.
https://www.drupal.org/node/1927648
This was actually the place to start. So if you haven’t, I’d go ahead and start there.
On the Angular side of things, I had a simple filereader directive that provided the base64 encoding of the file for you to then pass to a service call. I made minor modifications to this to better suit my needs, but ultimately used this already existing plugin. From there, I simply needed to make my service calls to create the file (a POST to entity/file).
eg
var file = {
"_links": { "type": {"href": serviceUrl + "rest/type/file/file"}},
"data": [{"value": $scope.formData.logo.file,}],
"uri" : [{"value": "public://" + $scope.formData.logo.name}],
"filemime": [{"value": $scope.formData.logo.type}],
"filename" : [{"value": $scope.formData.logo.name}],
"filesize" : [{"value": $scope.formData.logo.size}],
"status": [{"value": 1}],
"validators" : [{}]
};
File.create({}, file).$promise.then(function (response) {
....
});
Here's some code from Gizra
Here's some code from Gizra which whizzed past my twitter timeline yesterday - they've done a whole load of headless Drupal!
https://gizra.github.io/elm-hedley/#!/articles
https://github.com/Gizra/elm-hedley/blob/master/src/js/elm-interop.js