【发布时间】:2015-02-17 13:44:27
【问题描述】:
我是 Laravel 的新手,并尝试存储私有图像,以便只有经过身份验证的用户才能访问它们。首先,我将图像存储在 Public/UserImages 文件夹中。但是在这里,未经身份验证的用户也可以访问所有图片,方法是转到 Inspect Element of chrome,然后更改用户 ID。请帮帮我...
【问题讨论】:
我是 Laravel 的新手,并尝试存储私有图像,以便只有经过身份验证的用户才能访问它们。首先,我将图像存储在 Public/UserImages 文件夹中。但是在这里,未经身份验证的用户也可以访问所有图片,方法是转到 Inspect Element of chrome,然后更改用户 ID。请帮帮我...
【问题讨论】:
以下是我如何解决在 Laravel 5 中存储图像的问题,这样只有经过身份验证的用户才能查看图像。未通过身份验证的人员将被引导至登录页面。我的服务器是 Ubuntu/Apache2 服务器。
创建目录 /var/www/YOURWEBSITE/app/Assets/Images
将路由添加到app/Http/routes.php。
Route::get('/images/{file}','ImageController@getImage');
创建一个控制器app/Http/Controllers/ImageController.php
<?php
namespace App\Http\Controllers;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Auth;
class ImageController extends Controller {
public function __construct()
{
$this->middleware('auth');
}
public function getImage($filename) {
$path = '/var/www/YOURWEBSITE/app/Assets/Images/'.$filename;
$type = "image/jpeg";
header('Content-Type:'.$type);
header('Content-Length: ' . filesize($path));
readfile($path);
}
}
在您看来,您的 img 标签具有:
src="{{ url('/images/test.jpg') }}"
这当然假设 test.jpg 是 /var/www/YOURWEBSITE/app/Assets/Images/ 中的文件
您当然可以添加更多逻辑,例如不对图像的路径进行硬编码等。这只是一个实施身份验证的简单示例。请注意在控制器构造函数中使用中间件('auth')。
【讨论】:
这真的取决于你。它需要在public 目录之外——我个人会选择resources/uploads 或storage/uploads,或者使用cloud filesystem support 将它们完全存储在服务器外。
无论您选择什么,您都需要一个路由来获取文件并在首先检查用户是否具有访问权限后将其传递给用户。
【讨论】:
几天前我遇到了同样的问题并想出了这个解决方案:
您要做的第一件事是将文件上传到非公共目录。我的应用程序正在存储扫描的发票,所以我将把它们放在storage/app/invoices 中。上传文件和生成 url 的代码是:
// This goes inside your controller method handling the POST request.
$path = $request->file('invoice')->store('invoices');
$url = env('APP_URL') . Illuminate\Support\Facades\Storage::url($path);
返回的 url 应该是 http://yourdomain.com/storage/invoices/uniquefilename.jpg
现在您必须创建一个使用auth middleware 的控制器来确保用户通过身份验证。然后,定义一个从私有目录中获取文件并将其作为文件响应返回的方法。那将是:
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Storage;
class FileController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function __invoke($file_path)
{
if (!Storage::disk('local')->exists($file_path)) {
abort(404);
}
$local_path = config('filesystems.disks.local.root') . DIRECTORY_SEPARATOR . $file_path;
return response()->file($local_path);
}
}
最后一件事是在 routes/web.php 文件中注册路由:
Route::get('/storage/{file_name}', 'FileController')->where(['file_name' => '.*'])
所以你有了它,一个非常可重用的 sn-p 用于处理私有文件的所有项目:)
【讨论】:
要拥有私有文件(图像),您需要通过 route => 控制器 流来提供文件。您的身份验证中间件将处理身份验证和权限。如果需要进一步授权,请在控制器中处理。
在这里我们可以有一个处理所有文件的路由 [我个人不喜欢这样]。 我们可以使用这样的路由来做到这一点(就像通配符一样)。
Route::get('/storage/{filePath}', 'FileController@fileStorageServe')
->where(['filePath' => '.*'])
你也可以这样命名:
Route::get('/storage/{fileName}', 'FileController@fileStorageServe')
->where(['fileName' => '.*'])->name('storage.gallery.file');
否则我们为每种类型/类别的文件创建一个路由:(优势:您将能够更好地控制可访问性。 (每条路线和资源类型及其规则。如果你想用通配符路线(让我这么称呼它)来实现这一点,你需要有条件块(如果是这样,处理所有不同的情况。这是不必要的操作[直接去到右边的块,当路线分开时更好,加上它可以让您更好地组织权限处理])。
Route::get('/storage/gallery/{file}', 'System\FileController@getGalleryImage')
->name('storage.gallery.image');
外卡一号
<?php
public function fileStorageServe($file) {
// know you can have a mapping so you dont keep the sme names as in local (you can not precsise the same structor as the storage, you can do anything)
// any permission handling or anything else
// we check for the existing of the file
if (!Storage::disk('local')->exists($filePath)){ // note that disk()->exists() expect a relative path, from your disk root path. so in our example we pass directly the path (/.../laravelProject/storage/app) is the default one (referenced with the helper storage_path('app')
abort('404'); // we redirect to 404 page if it doesn't exist
}
//file exist let serve it
// if there is parameters [you can change the files, depending on them. ex serving different content to different regions, or to mobile and desktop ...etc] // repetitive things can be handled through helpers [make helpers]
return response()->file(storage_path('app'.DIRECTORY_SEPARATOR.($filePath))); // the response()->file() will add the necessary headers in our place (no headers are needed to be provided for images (it's done automatically) expected hearder is of form => ['Content-Type' => 'image/png'];
// big note here don't use Storage::url() // it's not working correctly.
}
每条路线一个
(差别很大,是参数,现在只引用文件名,而不是存储磁盘根目录的相对路径)
<?php
public function getCompaniesLogo($file) {
// know you can have a mapping so you dont keep the sme names as in local (you can not precsise the same structor as the storage, you can do anything)
// any permission handling or anything else
$filePath = config('fs.gallery').DIRECTORY_SEPARATOR.$file; // here in place of just using 'gallery', i'm setting it in a config file
// here i'm getting only the path from the root (this way we can change the root later) / also we can change the structor on the store itself, change in one place config.fs.
// $filePath = Storage::url($file); <== this doesn't work don't use
// check for existance
if (!Storage::disk('local')->exists($file)){ // as mentionned precise relatively to storage disk root (this one work well not like Storage:url()
abort('404');
}
// if there is parameters [you can change the files, depending on them. ex serving different content to different regions, or to mobile and desktop ...etc] // repetitive things can be handled through helpers [make helpers]
return response()->file(storage_path('app'.DIRECTORY_SEPARATOR.($file))); // the response()->file() will add the necessary headers in our place
}
现在您可以通过形成正确的 url 来检查(通过文件名转到存储副本,并形成您的路线。它应该向您显示图像)
最后一件事:
通配符一
<img src="{{route('routeName', ['fileParam' => $storageRelativePath])}}" />
请注意,上面示例中的routeName 将是storage.file,fileParam 将是filePath。 $storageRelativePath 例如,您从数据库中获取(通常就是这样)。
每条路线
<img src="{{route('routeName', ['fileName' => basename($storageRelativePath)])}}" />
相同,但我们只提供文件名。
注意事项: 发送此类响应的最佳方式是使用 response()->file();。 这就是您将在 5.7 文档中找到的内容。 与 Image::make($storagePath)->response(); 相比,该性能明智。除非您需要即时修改它。
你可以在medium查看我的文章:https://medium.com/@allalmohamedlamine/how-to-serve-images-and-files-privatly-in-laravel-5-7-a4b469f0f706
【讨论】: