File entities created by POSTing a base64-encoded image using the REST API are created as temporary files and cannot then be attached to another entity, and worse, are deleted after 6 hours.

This seems to be related to https://www.drupal.org/node/618654 or https://www.drupal.org/node/1028092 which are issues dating back 7 years, but surely this can't still be an unsolved problem.

I'm still getting to grips with D8 so I'm only tentatively poking around, but as far as I can tell the issue seems to be that FileEntityNormalizer.php is responsible for saving the POSTED file data in the denormalize method:

  public function denormalize($data, $class, $format = NULL, array $context = array()) {
    // Avoid 'data' being treated as a field.
    $file_data = $data['data'][0]['value'];
    unset($data['data']);
    // Decode and save to file.
    $file_contents = base64_decode($file_data);
    $entity = parent::denormalize($data, $class, $format, $context);
    $dirname = drupal_dirname($entity->getFileUri());
    file_prepare_directory($dirname, FILE_CREATE_DIRECTORY);
    if ($uri = file_unmanaged_save_data($file_contents, $entity->getFileUri())) {
      $entity->setFileUri($uri);
    }
    else {
      throw new \RuntimeException(SafeMarkup::format('Failed to write @filename.', array('@filename' => $entity->getFilename())));
    }
    return $entity;
  }

If I understand correctly, using file_unmanaged_save_data is ensuring the created file is temporary. I tried switching out that function for file_save_data which should make it a normal Drupal file, but that results in a 500 error:

Recoverable fatal error: Object of class Drupal\\file_entity\\Entity\\FileEntity could not be converted to string in /core/lib/Drupal/Core/Database/Statement.php on line 59

...and I'm not sure what that means, or how to fix that.

Looking at the code in file_save_data the relevant part that saves a permanent file looks like this:

if ($uri = file_unmanaged_save_data($data, $destination, $replace)) {
    // Create a file entity.
    $file = File::create([
      'uri' => $uri,
      'uid' => $user->id(),
      'status' => FILE_STATUS_PERMANENT,
    ]);
   ...
   file->save();

But the denormalize method doesn't seem to deal with file entities, so I can't modify it to set the status to FILE_STATUS_PERMANENT. I tried injecting this code into what looked like the appropriate place in the denormalize method, but I get the same error as when I switched file_unmanaged_save_data for file_save_data.

I'll keep poking around, but it seems like saving a file permanently using the REST API should be doable?

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

scott.whittaker created an issue. See original summary.

scott.whittaker’s picture

OK I've got a simple fix for this - add:

$entity->status = FILE_STATUS_PERMANENT;

just before the return $entity; in FileEntityNormalizer.denormalize. Patch attached.

uwieske’s picture

I am currently trying to submit a file (plus hal_json data) to Drupal 8.1.x REST API, but I am not able to set the status of this file to PERMANENT. Well actually Ii am not sure whether I am doing it wrongly or whether this feature is (still) simply not possible via the REST interface....?

Has this issue been fixed already? And does it relate to my use case above with Drupal 8.1.x REST API?
I have searched the web for more information on Drupal 8.1.x REST API inner workings with respect to REST, Uploaded File and File status, but I was not successful in getting much valuable information pointing to a solution.

http://stackoverflow.com/questions/38849454/which-drupal-8-1-x-rest-api-...

treasury’s picture

#2 works. Thank you @scott.whittaker.

cronos0’s picture

I have rewrite the patch for the last beta release, and correct the path reference for install with composer.

badrange’s picture

Status: Active » Needs review

Setting to "Needs review" since there is a patch.

The last submitted patch, 2: file_entity_save_permanent.patch, failed testing.

The last submitted patch, 2: file_entity_save_permanent.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 5: 8.x-2.0-beta3_file_entity_save_permanent.patch, failed testing.

Drupali’s picture

#2 did not work for me. I tried it on drupal 8.3.7.

Is there any progress on this issue ?

Ibn al-Hazardous’s picture

I took a stab at updating the patch for beta4.

imclean’s picture

joseph.olstad’s picture

Status: Needs work » Fixed
Berdir’s picture

I'm not sure this is the right fix and if it is a safe thing to do.

Also, core is going into a very different direction in regards to creating file entities over REST, a direction where this will not help I think.

  • joseph.olstad committed 744e5b8 on 8.x-2.x
    as per [#2756127-15], reverting commit from [#2756127-13]
    
joseph.olstad’s picture

Status: Fixed » Needs review

see comment #16 as per #15 reverting #13
setting to needs review.

joseph.olstad’s picture

Fonski’s picture

Re-created the patch from #11 to correct the diff path (atm it was not applicable through composer for example).

j1mb0b’s picture

Added setSize(). Since after PATCH it doesn't update correctly the file size. Also, it will FILE_EXISTS_REPLACE. Enforcing this is probably not a good idea, but it is suitable for my use-case.

Berdir’s picture

Status: Needs review » Closed (won't fix)

Sorry but I have no intentation of improving the this normalizer further.

Drupal core has an official API for creating files through REST now since 8.6 and you should use that: https://www.drupal.org/node/2941420.