Всем привет! Делаю полностью закрытый сайт, только для авторизованных пользователей. Для анонимов закрыто абсолютно всё. Но при этом доступно скачивание файлов из папки sites/default/files
$settings['file_private_path'] не помогает. Как закрыть полностью папку sites/default/files от анонимов?
Комментарии
А приватный метод загрузки включили?
Под рукой только настроенная под приватные файлы девятка. Там так:
Вроде бы с этим разобрались, если с нуля делать поле, то всё уходит в приватную папку. Всё работает, просто не так смотрела.
Но вот что делать например с сайтом, где уже достаточно много контента... С полями, у которых уже есть файлы в публичной папке, но которые надо бы скрыть?
В идеале нужен кастомный модуль, например, реагирующий на хук hook_post_update и/или запускающий батч-процесс по всем сущностям файлов, во время которого бы выполнялся перенос файла в приватную папку + коррекция/обновление URI в таблице файлов.
Но можно попробовать начать с этого: filefield_paths. У него заявляется такая фича как:
Это в теории добавляет настройки путей и чекбокс "Retroactive update" в форму настроек поля загрузки файла и при сохранении настроек должно осуществить перенос ранее загруженных файлов по указанному шаблону пути с фиксацией URI в БД.
Просто в тему: https://drupal.stackexchange.com/questions/264496/how-to-make-existing-p...
спасибо! Попробую
Теперь возник другой вопрос. Может сможете подсказать. Настроила приватную папку, всё ок. Настроила поле файл, теперь все загружаемые файлы уходят туда. Админу эти файлы доступны по ссылке, остальным нет.
Но в этой папке есть другая папка, в которую файлы грузятся не с полей, они там создаются программно. И админу ссылка на эти файлы выдает 403
Например сейчас есть папка 2025-06, в ней лежит файл, загруженный при создании материала. Он доступен по ссылке
Также рядом с этой папкой лежит папка mydir, в ней лежат программно сгенерированные файлы. Они по ссылке выдают 403
Права на папки и файлы такие же как у 2025-06
Тут всё просто: папка private у вас находится за пределами веб-рут, т.е. недоступна для прямого скачивания из браузера, поскольку не обслуживается веб-сервером. Для этого она (приватная папка), собственно, и нужна. Друпал умеет по запросу "забирать" из этой папки файлы и отдавать как файл соответствующего MIME-типа. Но это потому что он знает, какому запросу (урлу) какой физический приватный файл сопоставлен.
Если вы самостоятельно кладёте в такую папку какой-либо файл, то он не обслуживается Друпалом.
Кстати, уточнение: вы же создали папку за пределами корня сайта, верно?
Да, верно, папка есть и она за пределами корня. И в нее у меня прекрасно уходят файлы из полей сущностей, и они мне доступны по ссылке. И туда также автоматически генерируются файлы из самописного модуля. И мне бы хотелось иметь к ним доступный для админа url, то есть, чтобы он всё-таки обслуживался друпалом)
У меня что-то типа такого:
function mymodule_entity_presave(Drupal\Core\Entity\EntityInterface $entity) {
if ($entity->getEntityTypeId() == 'node' && $entity->getType() == 'mydocs') {
$phpWord = new \PhpOffice\PhpWord\PhpWord();
$_doc = new \PhpOffice\PhpWord\TemplateProcessor('private://mydir/templates/template.docx');
$_doc->setValue('Title', $entity->getTitle() );
$PATH = 'private://mydir/file'. $entity->id().'.docx';
$_doc->saveAs($PATH);
}
}
И судя по всему, вот эту часть с сохранением мне надо делать как-то по-другому, чтобы друпал брал этот файл к себе "в подопечные" и заносил в базу
Вместо $_doc->saveAs($PATH); пробую такое:
$fileRepository = \Drupal::service('file.repository');
$fileRepository->writeData($_doc, 'private://mydir/file'. $entity->id().'.docx', FileSystemInterface::EXISTS_REPLACE);
Но это выдает ошибку:
TypeError: Drupal\file\FileRepository::writeData(): Argument #1 ($data) must be of type string
Вот и не понимаю, как быть
1. Честно говоря, мне этот выкрутас (применение методов 'file.repository' в вашей реализации) кажется каким-то сомнительным. Но нет времени разбирать-уточнять вопрос в документации. Тем более, что постановка файла под управление Друпала (а-ля managed files) несколько изменилась после 9-ки. Я навскидку уже не помню. Но если хотите - пробуйте как наметили и тогда см. п.2 ниже.
2. Ключевая ошибка здесь то, что вы пытаетесь передавать объект $_doc (который является частным экземпляром класса \PhpOffice\PhpWord\TemplateProcessor как аргумент в метод $fileRepository->writeData. Но этот метод ожидает строку, а не объект.
То есть, чтобы эта функция приняла сгенерированный Word - нужно передать его содержимое именно как строку. Вместе с тем, насколько я понял, в классах \PhpOffice\PhpWord нет готовых методов для экспорта в переменную, только в физический файл.
Поэтому можно попробовать применить какой-то хак, чтобы захватить вывод и перенаправить его в переменную. Например, используя буферизацию PHP и манипуляцию с врэпперами/потоками вывода (в данном случае 'php://output'):
<?php
...
ob_start();
$_doc->saveAs('php://output');
$generated_document = ob_get_clean(); $fileRepository = \Drupal::service('file.repository');
$fileRepository->writeData(generated_document , 'private://mydir/file'. $entity->id().'.docx', FileSystemInterface::EXISTS_REPLACE);
...
?>
Собственно всё как обычно. Одно получилось, но дальше всё равно не идет
Сделала вот так:
$uri = $PATH;
$data = file_get_contents($uri);
$fileRepository = \Drupal::service('file.repository');
$fileRepository->writeData($data, $uri, FileSystemInterface::EXISTS_REPLACE);
Всё отлично, друпал берет мои файлы, они появиляются в базе
Но админ снова видит 403
Решила продолжить в новой теме.
https://drupal.ru/node/146438
Тут уже далеко ушло от изначального вопроса)
Ну как бы я вам писал выше, что мне показались сомнительными манипуляции с 'file.repository'
По каким ссылкам админ вообще пытается запросить эти файлы? Какой вид/формат имеют URL'ы таких файлов?
Вот например ссылка на файл, который я гружу в ноду, и по этой ссылке файл открывается
mysite.ru/system/files/2025-06/1.jpg
А вот ссылка на мой генерируемый файл
mysite.ru/system/files/my-dir/myfile74.docx
Она выдает 403
Вообще было бы здорово обойтись php://output но проблема в том, что я генерирую файл на основе шаблона. И в этом случае есть возможность только сохранять новый полноценный файл. С этим тоже долго разбиралась, и как поняла при использовании PhpOffice\PhpWord\TemplateProcessor невозможно вывести файл через php://output
1. Я сверился таки с документацией - у вас (вроде бы) верное использование службы 'file.repository', именно она создаёт одновременно и физический файл и его файловую сущность в Друпале. Т.е. тут всё вроде бы верно.
2.
Решение с врэппером 'php://output' - просто короткий путь без создания промежуточных файлов. Раз решили вопрос через запись с последующим чтением (а-ля file_get_contents) - ну, пусть так, главное - работает в вашем случае.
3.
У меня тут ощущение какого-то подвоха. На всякий случай: откуда вы берёте этот URL? Я бы проверил, какой реально URL имеет файловая сущность с помощью какого-то такого кода:
<?php
// Во-первых, при сохранении получаем объект созданной файловой сущности в $file
$file = $fileRepository->writeData($data, $uri, FileSystemInterface::EXISTS_REPLACE); // И, например, записываем в журнал Друпала полученный канонический URL файла.
\Drupal::logger('my_module')->info($file->toUrl('canonical', ['absolute' => TRUE])->toString());
?>
В общем проблема была в том, что помимо самого файла и прав на него, надо еще иметь права на скачивание (для приватных файлов)
https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21File%21fi...
Пока получается не очень, но уже лучше, чем было )
1) У админа должны быть все права по умолчанию. Однако, вы писали, что у админа тоже нет возможности открыть файл по ссылке. Т.е. для админа никаких спец. манипуляций обычно не требуется.
Кстати, какой именно админ? Самый-самый толстый (uid=1)? Просто я когда-то сталкивался с тем, что админы с НЕ uid=1 порой обрабатывались иначе. Подозреваю, что баг каких-то модулей.
2) Что именно получается не очень? Вроде решаете же проблему.
Админ самый главный, первый. Он тоже получал 403
В общем сейчас всё разрешилось. Добавила вот:
mymodule_file_download(string $uri): ?array {
if (strpos($uri, 'private:') !== 0) {
return NULL;
}
if (!\Drupal::currentUser()->hasPermission('access private files')) {
return -1;
}
$file_repository = \Drupal::service('file.repository');
$file = $file_repository->loadByUri($uri);
$headers = file_get_content_headers($file);
return $headers;
}
а еще захожу в папку /sites/default/files/feeds и там тоже всё доступно для скачивания. Закрытые для анонимов материалы, импортируемые когда-то через csv лежат там, и их можно скачать неавторизованному пользователю. Поэтому очень хотелось бы как-то иметь возможность закрыть некоторые папки в публичной части