Laravel - 在存储中显示 PDF 文件而不强制下载?

Posted

技术标签:

【中文标题】Laravel - 在存储中显示 PDF 文件而不强制下载?【英文标题】:Laravel - display a PDF file in storage without forcing download? 【发布时间】:2014-11-14 07:21:47 【问题描述】:

我有一个 PDF 文件存储在 app/storage/ 中,我希望经过身份验证的用户能够查看此文件。我知道我可以让他们使用

下载它
return Response::download($path, $filename, $headers);

但我想知道是否有办法让他们直接在浏览器中查看文件,例如当他们使用带有内置 PDF 查看器的 Google Chrome 时。任何帮助将不胜感激!

【问题讨论】:

我相信你想要Response::make($pdf, statusCode, array('content-type' => 'application/pdf')) 这帮助我走上正轨,谢谢! @ben-swinburne 在下面发布了正确的答案,我已经接受了。 【参考方案1】:

2017 年更新

从 Other response types 下记录的 Laravel 5.2 开始,您现在可以使用文件助手在用户浏览器中显示文件。

return response()->file($pathToFile);

return response()->file($pathToFile, $headers);

Source/thanks to below answer

2014 年的过时答案

您只需将文件的内容发送给浏览器并告诉它内容类型,而不是告诉浏览器下载它。

$filename = 'test.pdf';
$path = storage_path($filename);

return Response::make(file_get_contents($path), 200, [
    'Content-Type' => 'application/pdf',
    'Content-Disposition' => 'inline; filename="'.$filename.'"'
]);

如果您使用Response::download,它会自动将 Content-Disposition 设置为附件,从而导致浏览器下载它。请参阅 this question 了解内联内容处置和附件之间的区别。

编辑:根据 cmets 中的请求,我应该指出,您需要在文件开头use Response 才能使用外观。

use Response;

如果 Response 没有别名为 Illuminate 的响应门面,则为完全限定的命名空间。

【讨论】:

您的方法不适用于.txt 文件。它下载文件。 大概你把内容类型改成了 text/plain 吧? 只是为了提供帮助,记住你可以使用 storage_path() 不连接的助手:$path = storage_path('app/file.txt')。见laravel.com/docs/5.2/helpers#method-storage-path Response => Illuminate\Support\Facades\Response::class 获取Illuminate\Contracts\Routing\ResponseFactory 的实例,该实例绑定在Illuminate\Routing\RoutingServiceProvider::registerResponseFactory()Illuminate\Routing\ResponseFactory 的容器中,它确实有一个make 方法......我刚刚尝试过@987654339 @ 在 L5.2 中,它按预期工作。您需要在文件顶部添加use Response。响应助手只是简单地执行app(ResponseFactory::class)->make(...) 如果你想显示特定光盘中的文件,那么你可以得到没有帮助的路径:$file = Storage::disk('business_documents')->path('/' . $filename);【参考方案2】:

从 Laravel 5.2 开始,您可以使用 File Responses 基本上你可以这样称呼它:

return response()->file($pathToFile);

它会在浏览器中将文件显示为 PDF 和内嵌图像。

【讨论】:

我仍然得到下载文件而不是预览【参考方案3】:

Laravel 5.5 中,您只需将 "inline" 作为下载函数的 disposition 参数传递:

return response()->download('/path/to/file.pdf', 'example.pdf', [], 'inline');

【讨论】:

我的英雄 file() 类型的响应,所以这正是我所需要的!【参考方案4】:

Ben Swinburne's answer 是绝对正确的 - 他应该得到积分!对我来说,尽管答案在 Laravel 5.1 中有点悬而未决,这让我进行了研究——而在 5.2(这激发了这个答案)中,有一种新方法可以快速完成。

注意:此答案包含支持 UTF-8 文件名的提示,但建议考虑跨平台支持!

在 Laravel 5.2 中,您现在可以这样做:

$pathToFile = '/documents/filename.pdf'; // or txt etc.

// when the file name (display name) is decided by the name in storage,
// remember to make sure your server can store your file name characters in the first place (!)
// then encode to respect RFC 6266 on output through content-disposition
$fileNameFromStorage = rawurlencode(basename($pathToFile));

// otherwise, if the file in storage has a hashed file name (recommended)
// and the display name comes from your DB and will tend to be UTF-8
// encode to respect RFC 6266 on output through content-disposition
$fileNameFromDatabase = rawurlencode('пожалуйста.pdf');

// Storage facade path is relative to the root directory
// Defined as "storage/app" in your configuration by default
// Remember to import Illuminate\Support\Facades\Storage
return response()->file(storage_path($pathToFile), [
    'Content-Disposition' => str_replace('%name', $fileNameFromDatabase, "inline; filename=\"%name\"; filename*=utf-8''%name"),
    'Content-Type'        => Storage::getMimeType($pathToFile), // e.g. 'application/pdf', 'text/plain' etc.
]);

在 Laravel 5.1 中,您可以通过带有 a Response Macro in the boot method 的服务提供者添加上述方法 response()->file() 作为后备(如果您将其设为类,请确保使用其在 config/app.php 中的命名空间进行注册)。开机方法内容:

// Be aware that I excluded the Storage::exists() and / or trycatch()
$factory->macro('file', function ($pathToFile, array $userHeaders = []) use ($factory) 

    // Storage facade path is relative to the root directory
    // Defined as "storage/app" in your configuration by default
    // Remember to import Illuminate\Support\Facades\Storage
    $storagePath         = str_ireplace('app/', '', $pathToFile); // 'app/' may change if different in your configuration
    $fileContents        = Storage::get($storagePath);
    $fileMimeType        = Storage::getMimeType($storagePath); // e.g. 'application/pdf', 'text/plain' etc.
    $fileNameFromStorage = basename($pathToFile); // strips the path and returns filename with extension

    $headers = array_merge([
        'Content-Disposition' => str_replace('%name', $fileNameFromStorage, "inline; filename=\"%name\"; filename*=utf-8''%name"),
        'Content-Length'      => strlen($fileContents), // mb_strlen() in some cases?
        'Content-Type'        => $fileMimeType,
    ], $userHeaders);

    return $factory->make($fileContents, 200, $headers);
);

你们中的一些人不喜欢 Laravel Facades 或 Helper Methods,但这个选择是你的。如果 Ben Swinburne's answer 不适合你,这应该会给你一些指导。

意见提示:您不应该将文件存储在数据库中。尽管如此,此答案仅在您删除 Storage 外观部分时才有效,将内容而不是路径作为第一个参数作为@BenSwinburne 答案的第一个参数。

【讨论】:

您的内容处置也是错误的。它应该是 'Content-Disposition' => 'inline; filename="'.$filename.'"' 并且假设文件名不包含引号。见w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.5.1 @Tobia 感谢您的意见!如果文件名包含空格、保留或非 ASCII / ISO-8859-1 字符,则引号很重要。有一个filename* 替代方案需要RFC 5987 中指定的编码。 RFC 6266 Sec-5 has more examples。但是,尊重 cross platform file name limitations(较旧的更受限制的),这对于大多数平台来说都不是问题。我会记住您对 PHP 解决方案的建议并更新我的答案!【参考方案5】:

我正在使用 Laravel 5.4 和 response()->file('path/to/file.ext') 打开例如浏览器中内联模式的 pdf。这很好用,但是当用户想要保存文件时,保存对话框会建议 url 的最后一部分作为文件名。

我已经尝试像 Laravel-docs 中提到的那样添加一个 headers-array,但这似乎并没有覆盖 file()-method 设置的 header:

return response()->file('path/to/file.ext', [
  'Content-Disposition' => 'inline; filename="'. $fileNameFromDb .'"'
]);

【讨论】:

我也面临同样的问题,说实话很令人沮丧。你找到解决问题的方法了吗? :) 我发现它确实覆盖了标题内容,但'Content-Disposition 只是一个风险规则,并非所有浏览器都适用。恐怕没有办法更改标签名称。【参考方案6】:

从 laravel 5.5 开始,如果文件存储在远程存储上

return Storage::response($path_to_file);

或者如果它是本地存储的,你也可以使用

return response()->file($path_to_file);

我建议使用 Storage 门面。

【讨论】:

【参考方案7】:

Ben Swinburne 的回答很有帮助。

下面的代码适用于像我这样在数据库中有PDF 文件的人。

$pdf = DB::table('exportfiles')->select('pdf')->where('user_id', $user_id)->first();

return Response::make(base64_decode( $pdf->pdf), 200, [
    'Content-Type' => 'application/pdf',
    'Content-Disposition' => 'inline; filename="'.$filename.'"',
]);

其中$pdf->pdf 是数据库中的文件列。

【讨论】:

您的内容处置也是错误的。它应该是 'Content-Disposition' => 'inline; filename="'.$filename.'"' 并且假设文件名不包含引号。见w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.5.1 任何将其文件内容存储在数据库中的人都应该回到学校/大学并了解这是一种糟糕的做法。 @ash 我倾向于同意,出于实际原因。 (管理一个小数据库和一个文件目录比管理一个包含文件内容的大数据库更容易;想想 rsync、备份等)但是你不应该做出这样的笼统陈述。将文件存储在数据库中确实在很多场景中都有实际用途。 @Tobia 说了这么多,当你有一个小数据库时,它是实用的。并非所有时间都在构建大型软件,至于我,我构建了一些来自动化我的工作。谢谢指正。【参考方案8】:

Laravel 5.6.*

$name = 'file.jpg';

存储在图像或 pdf 上

$file->storeAs('public/', $name );

下载图片或pdf

return response()->download($name);

查看图片或pdf

return response()->file($name);

【讨论】:

【参考方案9】:

首先检索文件名,然后在 Blade 文件中使用 anchor(a) 标签,如下所示。这也适用于图像视图。

<a href=" asset('storage/admission-document-uploads/' . $filename)" target="_blank"> view Pdf </a>;

【讨论】:

【参考方案10】:

检索文件$contents = Storage::get('file.jpg');

下载文件return Storage::download('file.jpg');

文件网址$url = Storage::url('file.jpg');

【讨论】:

以上是关于Laravel - 在存储中显示 PDF 文件而不强制下载?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 laravel 刀片中显示来自 public/file/filen_name 的 pdf 文件而不下载文件 [重复]

如何使用CakePdf创建和下载PDF,而不是在浏览器中显示它

如何在 laravel 的 iframe 中显示 pdf 文件?

在浏览器中显示文件而不显示其位置

强制文件 (PDF) 在 Laravel 4 中下载

如何在 laravel 5.6 中下载 pdf 文件?