D8 Ficheros (Files) e imágenes - pierregermain/MyDrupal GitHub Wiki

Trabajando con Ficheros en Drupal 8

Ficheros públicos y privados

Tipos:

A saber:

  • Las rutas se configuran desde el settings.php y se pueden ver desde /admin/config/media/file-system.
  • Por defecto sólo está activado la descarga pública de ficheros.
  • Para activar la descarga privada se hace desde el settings.php con $settings['file_private_path'] = '/home/username/private';.
  • La idea es que la ruta privada esté fuera del alcance de drupal.
  • El acceso privado es menos eficiente que el público.
  • para cambiar la ubicación de los ficheros públicos lo hacemos desde el settings.php descomentando $settings['file_public_path'] = 'sites/default/files'; donde la ruta no lleva barra al principio y es relativa a la instalación de drupal y debe ser accesible desde la web.

URIs

Al trabajar con ficheros expresamos las rutas en formato scheme://target donde:

  • scheme puede ser public o private

Ejemplo:

uri ruta
public://foo/bar/example.txt /sites/default/files/foo/bar/example.txt

Tipos de ficheros en Drupal

  • Managed (Gestionados): Representados por Entidades y gestionados con Entity API y los hooks de Drupal.
  • Unmanaged (No gestionados): No son entidades y sólo se pueden gestionar con funciones nativas de PHP.

Clase para trabajar con Ficheros

Servicio: file_system (clase FileSystem)

Ejemplo:

$file_system = \Drupal::service('file_system');

$uri = 'private://foo/bar/example.txt';

$directory = $file_system->dirname($uri);
//devuelve: 'private://foo/bar'

$scheme = $file_system->uriScheme($uri);
//devuelve: 'private'

$filename = $file_system->basename($uri);
//devuelve: 'example.txt

$file_system = \Drupal::service('file_system');

// crea el directorio foo
$file_system->mkdir('public://foo');

// crea los directorios /bar/baz con permisos 755
$file_system->mkdir('public://bar/baz', 0755, TRUE);

// elimina el directorio foo
$file_system->rmdir('public://foo');

Métodos para trabajar con ficheros

  • Ficheros no gestionados (Unmanaged): (Libro III p. 277)

    • file.inc Ahí vienen todos los métodos que se pueden usar. Muchos están deprecated por lo que hay que usar el servicio correspondiente.

    Ejemplo:

    //localiza todas las plantillas en el módulo forum
    $directory = \Drupal::root() . "/core/modules/forum";
    $files = file_scan_directory($directory, '/\.twig$/');
    
  • Ficheros gestionados (Managed): (Libro III p. 280)

    • File.php
    • Los métodos de file.inc se pueden aplicar a Ficheros Gestionados usando $file_storage = \Drupal::entityTypeManager()->getStorage('file');
    • Podemos usar la interfaz FileInterface
    • tablas:
      • file_managed: Guarda información cómo fid, uuid, langcode, uid, filename, etc.
      • file.usage

Ejemplo: Acceder al user_picture de un usuario

$currentUser = \Drupal::currentUser();
$user_storage = \Drupal::entityTypeManager()->getStorage('user');
$user = $user_storage->load($currentUser->id());

$image_id = $user->get('user_picture')->first()->getValue()['target_id'];

$file_storage = \Drupal::entityTypeManager()->getStorage('file');
$image = $file_storage->load($image_id);
$image_uri = $image->getFileUri();

Hooks

  • sólo para ficheros gestionados

Podemos usar los hooks genéricos de entidades (poniendo cómo type "file")

hook_ENTITY_TYPE_load($entities);
hook_ENTITY_TYPE_create($entity);;
hook_ENTITY_TYPE_presave($entity);
hook_ENTITY_TYPE_insert($entity);
hook_ENTITY_TYPE_update($entity);
hook_ENTITY_TYPE_predelete($entity);
hook_ENTITY_TYPE_delete($entity);

Además tenemos los siguientes hooks específicos:

hook_file_download($uri);
hook_file_mimetype_mapping_alter(&$mapping);
hook_file_copy(Drupal\file\FileInterface $file, Drupal\file\FileInterface $source);
hook_file_move(Drupal\file\FileInterface $file, Drupal\file\FileInterface $source);
hook_file_url_alter(&$uri);
hook_file_validate(Drupal\file\FileInterface $file);

Formularios con ficheros:

Elementos

  • file: Según la función que usemos podremos elegir que "file" sea managed o no managed.
    • managed: file_managed_save_data()
    • unmanaged: file_save_upload()
  • managed_file: no necesitamos usar la función file_save_upload()

Pasos para crear formulario con ficheros no gestionados (unmanaged):

1. Crear el route

fichero: mymodule.routing.yml

mymodule.unmanaged:
  path: '/mymodule/files/unmanaged'
  defaults:
    _form: '\Drupal\mymodule\Form\Unmanaged'
    _title: 'Unmanaged file upload'
  requirements:
    _permission: 'access content'

2. Crear el Formulario

Fichero: /scr/Form/Unmanaged.php

<?php

namespace Drupal\mymodule\Form;

use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

class Unmanaged extends FormBase {

  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['upload'] = [
      '#type' => 'file',
      '#title' => $this->t('PDF File'),
      '#description' => $this->t('Upload a PDF File.'),
    ];
    $form['actions'] = ['#type' => 'actions'];
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Upload file'),
    ];
    return $form;
  }

  public function getFormId() {
    return 'mymodule_unmanaged';
  }

  public function submitForm(array &$form,
                             FormStateInterface $form_state) {
    $directory = 'private://unmanaged';
    $destination = $directory . '/unmanaged.pdf';

    $file_system = \Drupal::service('file_system');
    $file_system->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY);

    // Get the file from the request
    $all_files = \Drupal::request()->files->get('files', array());
    $file = file_get_contents($all_files['upload']);

    if ($file_system->saveData($file, $destination, FileSystemInterface::EXISTS_REPLACE)) {
      \Drupal::messenger()->addStatus(t('File uploaded'));
    }
    else {
      \Drupal::messenger()->addError(t('Error writing file.'));
    }
  }

}

Pasos para crear Formulario con archivos gestionados

1. Crear el fichero routing

mymodule.managed:
  path: '/mymodule/files/managed'
  defaults:
    _form: '\Drupal\mymodule\Form\Managed'
    _title: 'Managed file upload'
  requirements:
    _permission: 'access content'

2. Crear el Formulario

  • Se crea campo tipo 'managed_file'.
  • Al subir el fichero por 'managed_file' el fichero queda guardado en la tabla 'file_managed' cómo temporal en estado status = 0.
  • Para que el fichero se quede guardado cómo status = 1 hay que añadirlo a la tabla 'file_usage'
  • Si queremos subir varios ficheros de golpe podemos usar la propiedad '#multiple' => TRUE
<?php

namespace Drupal\mymodule\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\file\FileUsage\DatabaseFileUsageBackend;
use Symfony\Component\DependencyInjection\ContainerInterface;

class Managed extends FormBase {

  protected $currentUser;

  protected $fileUsage;

  public function __construct(AccountInterface $current_user,
                              DatabaseFileUsageBackend $file_usage) {
    $this->currentUser = $current_user;
    $this->fileUsage = $file_usage;
  }

  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('current_user'),
      $container->get('file.usage')
    );
  }

  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['upload'] = [
      '#title' => $this->t('Upload file'),
      '#type' => 'managed_file',
      '#upload_location' => 'public://managed',
      '#upload_validators' => [
        'file_validate_extensions' => ['pdf'],
      ],
      '#required' => TRUE,
    ];
    $form['actions'] = ['#type' => 'actions'];
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Upload file'),
    ];
    return $form;
  }

  public function getFormId() {
    return 'mymodule_managed';
  }

  public function submitForm(array &$form, FormStateInterface $form_state) {
    $file_storage =
      \Drupal::entityTypeManager()->getStorage('file');
    foreach ($form_state->getValue('upload') as $fid) {
      $file = $file_storage->load($fid);
      $this->fileUsage->add($file, 'mymodule', 'user', $this->currentUser->id(), 1);
    }
  }

}

Control de permisos sobre archivos

Pasos:

  1. Crear el fichero de permisos
'download private file':
  title: Download private file
  description: Allow users to download private files
  1. Usar hook_file_download($uri)
  • Con hook_file_download($uri) también se pueden añadir cabeceras adicionales al archivo (nombre de archivo, tipo MIME, forzar descarga, etc.)
  • En este ejemplo comprobamos:
    • Que el fichero esté en la carpeta managed
    • Que el usuario tenga permiso "download private file"
  • En este ejemplo:
    • Devolvemos el fichero con nombre private.pdf
    • Le decimos al navegador que no guarde el fichero en la caché
/**
 * Implements hook_file_download().
 */
function mymodule_file_download($uri) {
  $file_system = \Drupal::service('file_system');
  $directory = $file_system->dirname($uri);
  if(strpos($directory, 'private://managed') !== FALSE) {
    if (\Drupal::currentUser()->hasPermission('download private file')) {
      // tiene acceso al archivo
      return [
        'Content-type' => 'application/pdf',
        'Content-disposition' => 'attachment; filename="private.pdf"',
        'Cache-Control' => 'private',
      ];
    } else {
      return -1; // no tiene acceso
    }
  } else {
    return NULL; //no gestionado por nuestro módulo
  }
}

Presentación de imágenes con estilos

En render array

$build['image'] = [
  '#theme' => 'image_style',
  '#style_name' => 'thumbnail',
  '#uri' => $image_uri,
];

donde:

  • #theme es la plantilla
  • #style_name es el nombre del estilo a ser usado

Atributos disponibles:

  • #width y #height
  • #title
  • #alt

En enlaces

$style = \Drupal::entityTypeManager()->getStorage('image_style')->load('thumbnail');
$url = $style->buildUrl('public://image.png');

En Twig

{% set test_image = {
  '#theme':'image_style',
  '#style_name': 'thumbnail',
  '#uri':'public://images/foo.jpg,
  '#alt':'Some text',
  '#attributes': { class: 'foo bar' },
} %}

{{ test_image }}

Alternativamente usar https://www.drupal.org/project/twig_tweak:

{{ 'public://images/foo.jpg' | image_style('thumbnail') }}

Efecto de Imágen personalizado

Más info en Libro III p. 290

Ejemplo: https://api.drupal.org/api/drupal/core!modules!image!src!Plugin!ImageEffect!DesaturateImageEffect.php/class/DesaturateImageEffect/8