rhand's avatar
Level 6

Media file is displayed with firs character missing

For loading media using the Laravel Media Manager we have our own media controller. This is now working with latest flysystem 3 and Laravel 9 but for some reason the files are loading with first character missing. Any idea what inside the code could be causing this:

...
$list = [];
  $dirList = $this->getFolderContent($dir);
  $storageFolders = array_filter($this->getFolderListByType($dirList, 'dir'), [$this, 'ignoreFiles']);
  $storageFiles = array_filter($this->getFolderListByType($dirList, 'file'), [$this, 'ignoreFiles']);
...
// files
foreach ($storageFiles as $file) {
    if ($file['mimetype'] === 'image/webp') {
        continue;
    }

    $path = $file['path'];
    $time = $file['timestamp'] ?? null;

    $data = [
        'name'                   => $file['filename'].'.'.$file['extension'],
        'type'                   => $file['mimetype'],
        'size'                   => $file['size'],
        'visibility'             => $file['visibility'],
        'path'                   => $this->resolveUrl($path),
        'storage_path'           => $path,
        'last_modified'          => $time,
        'last_modified_formated' => $this->getItemTime($time),
    ];
...

and getFolderListByType is:

...
protected function getFolderListByType($list, $type)
{
    $list   = collect($list)->where('type', $type);
    $sortBy = $list->pluck('basename')->values()->all();
    $items  = $list->values()->all();

    array_multisort($sortBy, SORT_NATURAL, $items);

    return $items;
}
..
0 likes
13 replies
Sinnbeck's avatar

Just so we understand better, can you give an example. What you expect and what you get

1 like
rhand's avatar
Level 6

@Sinnbeck I expect a file do be displayed with icon and name 100_iconen-modules.png but I get 00_iconen-modules.png with the 1 missing as the first number in this case as the file starts with a number.

Snapey's avatar

@rhand only affects starting numbers, not letters?

What string is in the database?

Is the filename accurate?

1 like
rhand's avatar
Level 6

@Snapey

Also files with names without numbers are affected. Example enu-meassurements-margin.webp . Webp files are not supposed to load either but they do. But this example misses an initial m

The string is inside a JSON string in the database and there it is complete:

{"...
100_iconen-modules.png","imageWidth":"150","imageHeight":"35","imageUnit":"px","imageStyleObject":{"width":"150px","height":"35px"},"captionStyleObject":{"width":"150px","position":"absolute"},"hasCaption":false,"captionText":"","wrap":"div","id":1114259,"editing":false,"isVisible":true,"shiftKey":false}
Sinnbeck's avatar

@rhand But the code seems to get it from the disk, not database?

$dirList = $this->getFolderContent($dir);
1 like
rhand's avatar
Level 6

@Sinnbeck getFolderContent() at vendor/ivenms/laravel-media-manager/src/App/Controllers/Modules/GetContent.php works with the disk (storageDisk)

protected function getFolderContent($folder, $rec = false)
    {
        return $this->storageDisk->listContents($folder)->map(function ($attributes) {
            $path = $attributes->path();
            $name = substr($path, strrpos($path, '/') + 1);
            $extention = substr($name, strrpos($name, '.') + 1);
            return [
                'path' => $path,
                'dirname' => str_replace("/$name", '', $path),
                'basename' => $name,
                'extension' => $extention,
                'filename' => substr($name, 0, strrpos($name, '.')),
                'timestamp' => $attributes->lastModified(),
                'size' => $attributes->isFile() ? $attributes->fileSize() : 0,
                'type' => $attributes->isFile() ? 'file' : 'dir',
                'mimetype' => $attributes->isFile() ? ($attributes->mimeType() ?: $this->getMimeFromExtention($extention)) : 'folder',
                'etag' => $attributes->extraMetadata()['ETag'] ?? null,
                'visibility' => $attributes->visibility()
            ];
        })->toArray();
    }

and that is connected at the beginning of the controller code:

...
app()['config']->set('filesystems.disks.public_upload.root', public_path('/uploads/'.$this->currentProject->id));
        $this->storageDisk = Storage::build(app('config')->get('filesystems.disks.public_upload'));

        $this->storageDiskInfo = app('config')->get("filesystems.disks.{$this->fileSystem}");
        $this->baseUrl = url('/uploads/'.$this->currentProject->id);
        $this->db = app('db')->connection($config['database_connection'])
                                ->table($config['table_locked']);
...
Sinnbeck's avatar

@rhand If you use dd($storageFiles) is the full name in the names there?

1 like
rhand's avatar
Level 6

@Sinnbeck hmm, both the basename we used as part of code to get file name and current 'name' => $file['filename'].'.'.$file['extension'], missing a first character, but path is fine. And for some reason dirname is equal to file name:

1 => array:11 [
    "path" => "100_iconen-modules.png"
    "dirname" => "100_iconen-modules.png"
    "basename" => "00_iconen-modules.png"
    "extension" => "png"
    "filename" => "00_iconen-modules"
    "timestamp" => 1662025425
    "size" => 1849
    "type" => "file"
    "mimetype" => "image/png"
    "etag" => null
    "visibility" => "public"
  ]

So 'name' => $file['dirname'].'.'.$file['extension'], does work and load file name completely. Just wonder if that is a good idea/fine and why dirname plays nice and filename nor basename do not..

Reading

...
$path = $attributes->path();
$name = substr($path, strrpos($path, '/') + 1);
...
    'dirname' => str_replace("/$name", '', $path),
    'basename' => $name,
    'extension' => $extention,
    'filename' => substr($name, 0, strrpos($name, '.'))
...

some more from the method getFolderContent()

Sinnbeck's avatar
Sinnbeck
Best Answer
Level 102

@rhand The problem seems to come when there are no / in the $path

Example

$path = 'foo.jpg';
$name = substr($path, strrpos($path, '/') + 1); //results in oo.jpg
1 like
rhand's avatar
Level 6

@Sinnbeck true.

Only main change we had was using

app()['config']->set('filesystems.disks.public_upload.root', public_path('/uploads/'.$this->currentProject->id));
 $this->storageDisk = Storage::build(app('config')->get('filesystems.disks.public_upload'));

instead of older:

$this->storageDisk = app('filesystem')->disk($this->fileSystem);
$this->storageDisk->getAdapter()->setPathPrefix(public_path('/uploads/'.$this->currentProject->id));

so perhaps the additional slash always loaded has been removed somehow. But then I decided to check new package changes. And yes when I checked new package getContents.php I saw it had been updated https://github.com/ivenms/laravel-media-manager/commit/2d396cfb911c77c619f5480d7f9ddbf179fa195d to work with new Flysystem some so perhaps issue there is that the change is causing issues with paths without /

May need to import the method in our controller and make changes accordingly.

rhand's avatar
Level 6

In the end added the method from package to controller and tweaked it some

protected function getFolderContent($folder, $rec = false)
    {
        return $this->storageDisk->listContents($folder)->map(function ($attributes) {
            $path = $attributes->path();
            $splitPath = explode('/', $path);
            $name = end($splitPath);
            $extention = substr($name, strrpos($name, '.') + 1);

            return [
                'path' => $path,
                'dirname' => str_replace("/$name", '', $path),
                'basename' => $name,
                'extension' => $extention,
                'filename' => substr($name, 0, strrpos($name, '.')),
                'timestamp' => $attributes->lastModified(),
                'size' => $attributes->isFile() ? $attributes->fileSize() : 0,
                'type' => $attributes->isFile() ? 'file' : 'dir',
                'mimetype' => $attributes->isFile() ? ($attributes->mimeType() ?: $this->getMimeFromExtention($extention)) : 'folder',
                'etag' => $attributes->extraMetadata()['ETag'] ?? null,
                'visibility' => $attributes->visibility()
            ];
        })->toArray();
    }

Using

$path = $attributes->path();
$splitPath = explode('/', $path);
$name = end($splitPath);

seems to help out.

thisisablock's avatar

Had recently the same issue. The reason was an upgrade to flysystem s3 adapter (^3). We had defined for the s3 adapter a root entry to specify a base folder as

'root' => '/folder/'

but after the s3 update all first chars of folders and files were removed. fix for that was to remove the slash in the laravel config

'root' => 'folder'
1 like

Please or to participate in this conversation.