roadrunner-php / laravel-bridge

πŸŒ‰ RoadRunner ⇆ Laravel bridge πŸ‡ΊπŸ‡¦β€οΈ
https://roadrunner.dev/docs/integration-laravel
MIT License
372 stars 25 forks source link

move_uploaded_file() not working #43

Closed francsiswanto closed 3 years ago

francsiswanto commented 3 years ago

Describe the bug

I tried this code:

    $file = request()->file('user');
    $file->move($targetDir, $newfilename);

will be get error:

Could not move the file \"Z:\\USERTEMP\\sym4B1D.tmp\" to \"E:\\UploadTest\\test\\200911000005\\200911000005-1.jpg\" ()

and I know this error comes from:

vendor/symfony/http-foundation/File/UploadedFile.php:
184            set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
185            $moved = move_uploaded_file($this->getPathname(), $target);
186            restore_error_handler();
187            if (!$moved) {
188                throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s).', $this->getPathname(), $target, strip_tags($error)));
189            }

My temporary solution:

    $file = request()->file('user');
    $targetPathName = $targetDir . DIRECTORY_SEPARATOR . $newfilename;
    try {
        $file->move($targetDir, $newfilename);
    } catch (\Exception $err) {
        try {
            rename($file->getPathname(), $targetPathName);
        } catch (\Exception $err2) {
            throw new \Symfony\Component\HttpFoundation\File\Exception\FileException($err->getMessage());
        }
    }

Expectation

Simply using $file->move(). Thank you.

System information

Key Value
PHP version 8.0.6
Current package version 4.0
RoadRunner version 2
Environment local on Windows platform

RoadRunner configuration file content

server:
  command: "php ./vendor/spiral/roadrunner-laravel/bin/rr-worker start"

http:
  address: 0.0.0.0:8080
  middleware: ["headers", "static", "gzip"]
  pool:
    max_jobs: 64 # feel free to change this
    supervisor:
      exec_ttl: 60s
  headers:
    response:
      X-Powered-By: "RoadRunner"
    cors:
      allowed_origin: "*"
      allowed_headers: "*"
      allowed_methods: "*"
      allow_credentials: true
      exposed_headers: "Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,Server-Timing"
      max_age: 82600
  static:
    dir: "public"
    forbid: [".php"]
  http2:
    h2c: false
    max_concurrent_streams: 128
  uploads:
#    dir: "z:\temp"
    forbid: [".php", ".exe", ".bat", ".sh"]

logs:
#  mode: production
#  level: error

reload:
  # sync interval
  interval: 1s
  # global patterns to sync
  patterns: [ ".php", ".env" ]
  # list of included for sync services
  services:
    http:
      # recursive search for file patterns to add
      recursive: true
      # ignored folders
      ignore: [ "vendor" ]
      # service specific file pattens to sync
      patterns: [ ".php", ".env", ".go", ".md" ]
      # directories to sync. If recursive is set to true,
      # recursive sync will be applied only to the directories in `dirs` section
      dirs: [ "." ]

Package configuration file content

<?php

use Spiral\RoadRunnerLaravel\Events;
use Spiral\RoadRunnerLaravel\Listeners;

return [
    /*
    |--------------------------------------------------------------------------
    | Force HTTPS Schema Usage
    |--------------------------------------------------------------------------
    |
    | Set this value to `true` if your application uses HTTPS (required for
    | example for correct links generation).
    |
    */

    'force_https' => (bool) env('APP_FORCE_HTTPS', false),

    /*
    |--------------------------------------------------------------------------
    | Containers Pre Resolving
    |--------------------------------------------------------------------------
    |
    | Declared here abstractions will be resolved before events loop will be
    | started.
    |
    */

    'pre_resolving' => [
        'auth',
        'cache',
        'cache.store',
        'config',
        'cookie',
        'db',
        'db.factory',
        'encrypter',
        'files',
        'hash',
        'log',
        'router',
        'routes',
        'session',
        'session.store',
        'translator',
        'url',
        'view',
    ],

    /*
    |--------------------------------------------------------------------------
    | Event Listeners
    |--------------------------------------------------------------------------
    |
    | Worker provided by this package allows to interacts with request
    | processing loop using application events. Feel free to add your own event
    | listeners.
    |
    */

    'listeners' => [
        Events\BeforeLoopStartedEvent::class => [
            Listeners\FixSymfonyFileValidationListener::class,
        ],

        Events\BeforeLoopIterationEvent::class => [
            Listeners\EnableHttpMethodParameterOverrideListener::class,
            Listeners\RebindHttpKernelListener::class, // Laravel 7 issue: <https://git.io/JvPpf>
            Listeners\RebindViewListener::class,
            Listeners\RebindAuthorizationGateListener::class,
            Listeners\RebindBroadcastManagerListener::class,
            Listeners\RebindDatabaseManagerListener::class,
            Listeners\RebindMailManagerListener::class,
            Listeners\RebindNotificationChannelManagerListener::class,
            Listeners\RebindPipelineHubListener::class,
            Listeners\RebindQueueManagerListener::class,
            Listeners\RebindValidationFactoryListener::class,
            Listeners\CloneConfigListener::class,
            Listeners\UnqueueCookiesListener::class,
            Listeners\FlushAuthenticationStateListener::class,
            Listeners\ResetSessionListener::class,
            Listeners\ResetProvidersListener::class,
            Listeners\ResetLocaleStateListener::class,

            // Listeners\ResetLaravelScoutListener::class, // for 'laravel/scout' package
            // Listeners\ResetLaravelSocialiteListener::class, // for 'laravel/socialite' package
            // Listeners\ResetInertiaListener::class, // for 'inertiajs/inertia-laravel' package
        ],

        Events\BeforeRequestHandlingEvent::class => [
            Listeners\RebindRouterListener::class,
            Listeners\InjectStatsIntoRequestListener::class,
            Listeners\BindRequestListener::class,
            Listeners\ForceHttpsListener::class,
            Listeners\SetServerPortListener::class,
        ],

        Events\AfterRequestHandlingEvent::class => [
            //
        ],

        Events\AfterLoopIterationEvent::class => [
            Listeners\FlushArrayCacheListener::class,
            Listeners\ResetDatabaseRecordModificationStateListener::class,
            Listeners\ClearInstancesListener::class,
            Listeners\RunGarbageCollectorListener::class,
        ],

        Events\AfterLoopStoppedEvent::class => [
            //
        ],

        Events\LoopErrorOccurredEvent::class => [
            Listeners\SendExceptionToStderrListener::class,
            Listeners\StopWorkerListener::class,
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Instances Clearing
    |--------------------------------------------------------------------------
    |
    | Instances described here will be cleared on every request (if
    | `ClearInstancesListener` is enabled).
    |
    */

    'clear_instances' => [
        'auth', // is not required for Laravel >= v8.35
    ],

    /*
    |--------------------------------------------------------------------------
    | Reset Providers
    |--------------------------------------------------------------------------
    |
    | Providers that will be registered on every request (if
    | `ResetProvidersListener` is enabled).
    |
    */

    'reset_providers' => [
        Illuminate\Auth\AuthServiceProvider::class, // is not required for Laravel >= v8.35
        // App\Your\Custom\AuthServiceProvider::class,
        Illuminate\Pagination\PaginationServiceProvider::class, // is not required for Laravel >= v8.35
        App\Libs\Passport\PassportServiceProvider::class,
        App\Libs\ServerTiming\ServerTimingServiceProvider::class,

    ],
];

Additional context

Add any other context about the problem here.

tarampampam commented 3 years ago

Hi @francsiswanto! Thanks for your issue!

As you probably know - some native PHP functions do not work as expected under roadrunner, for example, we need to "hack" is_uploaded_file for the Symfony:

https://github.com/spiral/roadrunner-laravel/blob/cdef0a11b4ab80207db6ec0ae8364ad6f48132d2/fixes/fix-symfony-file-validation.php#L28-L31

And your case looks very similar. We can:

What do you think?

francsiswanto commented 3 years ago

Hi @tarampampam,

Since there is such a fact, I think the solution you mention makes sense to me. Thanks for the feed back.

tarampampam commented 3 years ago

I will make a fix after #44 merging and releasing, ok?

tarampampam commented 3 years ago

@francsiswanto Could you please to test these changes?

tarampampam commented 3 years ago

Fix was successfully tested in a PR https://github.com/tarampampam/laravel-roadrunner-in-docker/pull/34

tarampampam commented 3 years ago

πŸ”₯ Released in 5.0.1

francsiswanto commented 3 years ago

@francsiswanto Could you please to test these changes?

@tarampampam, the result is super awesome :), $file->move() now worked as expected. Thank you.