1

Introduction

I am using:

  • Windows 10 Pro
  • XAMPP with PHP v7.0.9
  • Symfony v3.1.6
  • Doctrine v2.5.4
  • StofDoctrineExtensionsBundle [1] in order to manage Tree structure.
  • OneupUploaderBundle [2] in order to Upload files
  • OneupFlysystemBundle [3] for filesystem abstraction

Setting up

I did setup the tree structure (that represents directories and files) and file upload with OneupUploaderBundle and Flysystem endpoint.

File upload is organized to upload to folder named data that exists in project root folder. Uploading is working fine and even paths with custom sub-directories for different uploads work (for example data/project_001/test1.txt and data/project_002/test2.txt)!

Problem

I need to serve files that users had uploaded earlier.

At the moment

  • I am getting error (with controller ending 1)

    The file "Project_9999/2_Divi/bbb.pdf" does not exist
    500 Internal Server Error - FileNotFoundException
    
  • and error (with controller ending 2)

    Catchable Fatal Error: Object of class League\Flysystem\File could not be converted to string
    500 Internal Server Error - ContextErrorException
    

Note that $exists return true - so file is definitely there!

Code

relevant part of config.yml

oneup_uploader:
    mappings:
        gallery:
            storage:
                type: flysystem
                filesystem: oneup_flysystem.gallery_filesystem

            frontend: blueimp
            enable_progress: true
            namer: app.upload_unique_namer

            allowed_mimetypes: [ image/png, image/jpg, image/jpeg, image/gif ]
            max_size: 10485760s

oneup_flysystem:
    adapters:
        my_adapter:
            local:
                directory: "%kernel.root_dir%/../data"

    filesystems:
        gallery:
            adapter: my_adapter

Controller action

<?php

public function projectDownloadFileAction($file_id, Request $request)
{
    $em = $this->getDoctrine()->getManager();
    $repo = $em->getRepository('AppBundle:FileTree');
    $ultra = $this->get('app.ultra_helpers');

    $selected_file = $repo->getFileTreeNodeByIdArray($file_id);
    $item_name = $selected_file[0]['item_name'];
    $item_extension = $selected_file[0]['item_extension'];

    $activeProject = $this->get('session')->get('active_project');
    $activeProjectSelectedNodePath = $this->get('session')->get('active_project_selected_node_path');
    $item_file_name = $item_name .'.'. $item_extension;
    $complete_file_path = $activeProject['secret_path'] . $activeProjectSelectedNodePath .'/'. $item_file_name;
    dump($complete_file_path);

    ENDING -1-
    ENDING -2-
}

Ending 1

    $downloadable_file = new \SplFileInfo($complete_file_path);
    dump($downloadable_file);
    $response = new BinaryFileResponse($downloadable_file);

    // Set headers
    $response->headers->set('Cache-Control', 'private');
    $response->headers->set('Content-Type', mime_content_type($downloadable_file));
    $response->headers->set('Content-Disposition', $response->headers->makeDisposition(
        ResponseHeaderBag::DISPOSITION_ATTACHMENT,
        $downloadable_file->getFilename()
    ));

    return $response;

Ending 2

    $filesystem = $this->get('oneup_flysystem.gallery_filesystem');
    $exists = $filesystem->has($complete_file_path);
    if (!$exists)
    {
        throw $this->createNotFoundException();
    }
    dump($exists);

    $downloadable_file = $filesystem->get($complete_file_path);
    dump($downloadable_file);

    $response = new BinaryFileResponse($complete_file_path);
    $response->trustXSendfileTypeHeader();
    $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT);

    return $response;

Update 1

I tried to supply absolute path to the Ending 1 variable `$complete_file_path, but got only errors. For example:

  • File not found at path: C:\DEV\tree_and_upload\data\Project_9999\1_Viens\test1.txt 500 Internal Server Error - FileNotFoundException

  • File not found at path: C:/DEV/tree_and_upload/data/Project_9999/1_Viens/test1.txt 500 Internal Server Error - FileNotFoundException

But it did not make any diference - I got access error. Perhaps Symfony is actively restricting access to data folder in the root of the project...

Question

How do one would serve files from data folder that is situated in project root folder and using Flysystem filesystem abstraction layer with Local adapter?

Conclusion

What am I missing?

Please advise.

Thank you for your time and knowledge.

Rikijs
  • 728
  • 1
  • 12
  • 48
  • Does `$complete_file_path` really contain an absolute path to the file or is it relative to `%kernel.root_dir%/../data`? – xabbuh Nov 17 '16 at 13:11
  • @xabbuh, yes `$complete_file_path` is relative to `%kernel.root_dir%/../data`. – Rikijs Nov 18 '16 at 13:42
  • You will then either have to prepend `$complete_file_path` with `%kernel.root_dir%/../data` when constructing the `BinaryFileResponse` or check if there is something like a `getAbsolutePath()` method in the `$filesystem` object. – xabbuh Nov 18 '16 at 18:29
  • @xabbuh, As it turns out there are no methods to get absolute path to the file in `Flysystem` with `local` adapter. It is proposed to move files somewhere to `web` directory to make them accessible or use streams. Indeed - streams allow to serve files from `data` folder that exists in the root of the project. So I ended up using Symfony's `StreamedResponse` and it works beautifully. Thanks for the help/ – Rikijs Nov 18 '16 at 21:40

1 Answers1

1

As Flysystem with local adapter has no methods to get downloadable file path! It is suggested to move or store files in the public web folder and retrieve path the same way as with assets.

I ended up using StreamedResponse [1] instead of BinaryFileResponse.

use Symfony\Component\HttpFoundation\StreamedResponse;

$response = new StreamedResponse();
$response->setCallback(function () {
    echo $downloadable_file_stream_contents;
    flush();
});
$response->send();

How do you get $downloadable_file_stream_contents;

$fs = new Filesystem($localAdapter);
$downloadable_file_stream = $fs->readStream($public_path);
$downloadable_file_stream_contents = stream_get_contents($downloadable_file_stream);
Rikijs
  • 728
  • 1
  • 12
  • 48
  • How do you get `$downloadable_file_stream_contents;`? – goto Apr 06 '17 at 12:31
  • doing so, you will trigger php event for static files, except if you need a custom logic before delivering the file (ie : authorisation layer), it is not a good practice, you should consider storing them under the web directory. – Bruno May 30 '17 at 09:12
  • @bruno Yes, I need to protect files in `data` folder and serve them only in certain cirsumstances (users, groups). Shared files are copied and stored in `web` directory. – Rikijs May 30 '17 at 10:36