Skip to content

Instantly share code, notes, and snippets.

@bnchdrff
Created July 21, 2016 13:30
Show Gist options
  • Save bnchdrff/a388b763bc97f7a1c6107698652cc58d to your computer and use it in GitHub Desktop.
Save bnchdrff/a388b763bc97f7a1c6107698652cc58d to your computer and use it in GitHub Desktop.
Add POST endpoint for performant Drupal 8 REST file uploads

Instead of accepting serialized files, I wanted to process file uploads as multipart/form-data.

I also wanted to use the crsf prevention API.

This is what I came up with. Replace modulename with your module and fileupload with whatever you want to name your class/endpoint.

In your routing.yml:

modulename.fileupload:
  path: 'fileupload'
  defaults: { _controller: '\Drupal\modulename\Controller\FileUploadController::create' }
  methods:  [POST]
  requirements:
    _custom_access: '\Drupal\modulename\Controller\FileUploadController::access'

In your services.yml:

services:
  modulename.route_subscriber:
    class: Drupal\modulename\Routing\RouteSubscriber
    tags:
      - { name: event_subscriber }

In src/Routing/RouteSubscriber.php:

<?php
namespace Drupal\modulename\Routing;

use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;

/**
 * Alter modules's route(s).
 */
class RouteSubscriber extends RouteSubscriberBase {
  /**
   * {@inheritdoc}
   *
   * Add a CSRF-Token requirement to the fileupload route.
   */
  public function alterRoutes(RouteCollection $collection) {
    if ($route = $collection->get('modulename.fileupload')) {
      $route->setRequirement('_access_rest_csrf', 'TRUE');
    }
  }
}

In src/Controller/FileUploadController.php

<?php
namespace Drupal\modulename\Controller;

use Drupal\Core\Session\AccountInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Drupal\Core\Access\AccessResult;

/**
 * Process file uploads.
 */
class FileUploadController {

  /**
   * Allow access for logged-in, authenitcated users.
   *
   * @param \Drupal\Core\Session\AccountInterface $account
   *   Run access checks for this account.
   */
  public function access(AccountInterface $account) {
    return AccessResult::allowedIf($account->isAuthenticated());
  }

  /**
   * Process posted files.
   */
  public function create(Request $request) {
    if (strpos($request->headers->get('Content-Type'), 'multipart/form-data;') !== 0) {
      $res = new JsonResponse();
      $res->setStatusCode(400, 'must submit multipart/form-data');
      return $res;
    }

    $data = file_get_contents($_FILES['image']['tmp_name']);
    $mime = $_FILES['image']['type'];

    $file = file_save_data($data, NULL, FILE_EXISTS_REPLACE);

    $response['file_id'] = $file->id();

    return new JsonResponse($response);
  }


}
@stillfinder
Copy link

Hi, could you please provide an example of the request?

@jasonyarrington
Copy link

jasonyarrington commented Mar 4, 2021

I adopted this code for use on a site. Here is some sample code of a jQuery request.

var form = new FormData();
form.append("excel", fileInput.files[0], "Testdata.xlsx");

var settings = {
  "url": "<URL>",
  "method": "POST",
  "timeout": 0,
  "headers": {
    "Accept": "application/json",
    "Content-Type": "application/octet-stream"
  },
  "processData": false,
  "mimeType": "multipart/form-data",
  "contentType": false,
  "data": form
};

$.ajax(settings).done(function (response) {
  console.log(response);
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment