支持Laravel.io的持续开发 →

使用Laravel发送、验证和存储Base64文件

2024年2月12日 阅读时间:6分钟

Laravel使发送和上传文件变得简单,至少在处理二进制文件时是这样。您通常可以使用HTML表单,并设置multipart/form-data编码类型来发送二进制文件。在您的控制器中,您可以简单地将文件获取如下:$request->file('image')并在磁盘上保存。您接收到的文件将返回Illuminate\Http\UploadedFile类的实例,它为您提供了许多方便的方法来进一步操作收到的文件。这很简单和直接,对吧?

但如果您想要通过JSON请求发送和接收文件呢?事情会变得有点棘手,因为JSON请求无法传输二进制数据。您必须将文件转换为Base64字符串,然后在使用应用程序进行处理以提取所需数据的。让我们看看如何操作。

<h2 class="subheading" id="covert-base64">将图像转换为Base64</h2>

首先,让我们看看如何将选择的图像转换为Base64字符串。当然,这可以通过编程方式完成,但为了简单起见,我们只是将简单的PNG图像上传到<a title="转换图像" href="https://base64.guru/converter/encode/image" target="_blank" rel="noopener noreferrer nofollow">Base64 Guru网站</a>(当然,您可以使用任何其他网站),并将输出保存供以后使用。 

以下是一个示例,展示其可能的外观

<a title="图像转换" href="https://geoligard.com/storage/posts/January2024/covert-file.png" target="_blank"><img title="图像转换" src="https://geoligard.com/storage/posts/January2024/covert-file.png" alt="图像转换" width="100%" height="auto"></a>

<h2 class="subheading" id="postman-example">Postman 示例</h2>

现在,在我们继续之前,让我们看看 Postman 中这种请求的完整示例。简而言之,我们将发送一个带有单个 image 参数的原始 JSON POST 请求,然后我们将得到新存储文件的 URL。

它可能看起来像这样

<a title="Postman 示例" href="https://geoligard.com/storage/posts/January2024/base64-postman-example_1.png" target="_blank"><img title="Postman 示例" src="https://geoligard.com/storage/posts/January2024/base64-postman-example_1.png" alt="Postman 示例" width="100%" height="auto"></a>

<h2 class="subheading" id="initial-setup">初始设置</h2>

我们知道我们会期待什么。让我们创建一个控制器和一个路由来处理请求。

<pre><code class="language-bash">php artisan make:controller Base64Controller</code></pre> <pre><code class="language-php">// api.php use App\Http\Controllers\Base64Controller; Route::post('base64', [Base64Controller::class, 'index']);</code></pre>

到目前为止,一切都很好。我们的 base64 路由将针对 Base64Controller 中的 index 方法。接下来,我们将在 index 方法中添加一些代码来处理 JSON 请求。

// Base64Controller.php

namespace App\Http\Controllers;

use Illuminate\Http\File;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;

class Base64Controller extends Controller
{
    public function index()
    {
        if (request()->isJson() && !empty(request()->input('image'))) {
            $base64Image = request()->input('image');

            if (!$tmpFileObject = $this->validateBase64($base64Image, ['png', 'jpg', 'jpeg', 'gif'])) {
                return response()->json([
                    'error' => 'Invalid image format.'
                ], 415);
            }

            $storedFilePath = $this->storeFile($tmpFileObject);

            if(!$storedFilePath) {
                return response()->json([
                    'error' => 'Something went wrong, the file was not stored.'
                ], 500);
            }

            return response()->json([
                'image_url' => url(Storage::url($storedFilePath)),
            ]);
        }

        return response()->json([
            'error' => 'Invalid request.'
        ], 400);
    }

我们首先检查是否是 JSON 请求并且图像参数是否不为空。然后验证并上传图像。最后,我们返回新上传文件的 URL。很简单,对吧?你可能已经注意到,我们定义了两个辅助函数,validateBase64storeFile,这些函数可以验证并为我们存储文件。现在定义它们的时候到了。

<h2 class="subheading" id="helper-functions">辅助函数</h2>

第一个函数 (validateBase64) 只是检查 image 参数是否是图像的 Base64 表示。它将接受一个 Base64 字符串和一个允许的 MIME 类型数组。如果没有验证失败,将返回临时文件对象。这个函数主要是基于这个 <a title="验证 base64" href="https://stackoverflow.com/questions/51419310/validating-base64-image-laravel/52914093#52914093" target="_blank" rel="noopener noreferrer nofollow">Stack Overflow 答案</a>。

// Base64Controller.php

/**
 * Validate base64 content.
 *
 * @see https://stackoverflow.com/a/52914093
 */
private function validateBase64(string $base64data, array $allowedMimeTypes)
{
    // strip out data URI scheme information (see RFC 2397)
    if (str_contains($base64data, ';base64')) {
        list(, $base64data) = explode(';', $base64data);
        list(, $base64data) = explode(',', $base64data);
    }

    // strict mode filters for non-base64 alphabet characters
    if (base64_decode($base64data, true) === false) {
        return false;
    }

    // decoding and then re-encoding should not change the data
    if (base64_encode(base64_decode($base64data)) !== $base64data) {
        return false;
    }

    $fileBinaryData = base64_decode($base64data);

    // temporarily store the decoded data on the filesystem to be able to use it later on
    $tmpFileName = tempnam(sys_get_temp_dir(), 'medialibrary');
    file_put_contents($tmpFileName, $fileBinaryData);

    $tmpFileObject = new File($tmpFileName);

    // guard against invalid mime types
    $allowedMimeTypes = Arr::flatten($allowedMimeTypes);

    // if there are no allowed mime types, then any type should be ok
    if (empty($allowedMimeTypes)) {
        return $tmpFileObject;
    }

    // Check the mime types
    $validation = Validator::make(
        ['file' => $tmpFileObject],
        ['file' => 'mimes:' . implode(',', $allowedMimeTypes)]
    );

    if($validation->fails()) {
        return false;
    }

    return $tmpFileObject;
}

太好了,现在让我们定义第二个辅助函数,该函数将处理从上一个函数返回的临时文件对象并上传文件。

// Base64Controller.php

/**
 * Store the temporary file object
 */
private function storeFile(File $tmpFileObject)
{
    $tmpFileObjectPathName = $tmpFileObject->getPathname();

    $file = new UploadedFile(
        $tmpFileObjectPathName,
        $tmpFileObject->getFilename(),
        $tmpFileObject->getMimeType(),
        0,
        true
    );

    $storedFile = $file->store('images/base64', ['disk' => 'public']);

    unlink($tmpFileObjectPathName); // delete temp file

    return $storedFile;
}

就这样,我们完成了。如果您想从 <code>Base64Controller.php</code> 复制整个代码,请访问 <a title="Gist 示例" href="https://gist.github.com/goran-popovic/d5af7366af2a7f6f9b04ebc4a48a4e7e" target="_blank" rel="noopener noreferrer nofollow">Gist</a>。

<h2 class="subheading" id="conclusion">结论</h2>

今天我们创建了一些很棒的功能,可以让我们接受、验证和存储 Base64 编码的文件。在我们的示例中,我们正在验证图像,但您也可以将这些函数用于其他类型的文件,如 .txt.pdf 等。此外,为了简化,所有代码都添加到同一个控制器中,但您也可以将这些函数移动到自定义辅助函数文件中,或者创建一个 trait,然后您可以轻松地在需要处理 Base64 文件时重用它们。

最后更新 5 个月前。

driesvints, karimalik, ol-serdiuk 赞了这个文章

3
喜欢这篇文章? 让作者知道,并为他们鼓掌!
geoligard (Goran Popović) 软件开发人员,喜欢使用 Laravel,在 geoligard.com 上写博客。

你可能喜欢的其他文章

2024年3月11日

如何使用 Larastan 将你的 Laravel 应用从 0 到 9 升级

在 Laravel 应用执行之前找到错误是可能的,这要归功于 Larastan...

阅读文章
2024年7月19日

无需使用 traits 标准化 API 响应

我注意到大多数用于 API 响应的库都是使用 traits 实现的...

阅读文章
2024年7月17日

通过 Discord 通知在你的 Laravel 项目中收集反馈

如何在一个 Laravel 项目中创建一个反馈模块并在接收到 Discord...

阅读文章

我们感谢这些 卓越的公司 对我们的支持

您的标志在这里?

Laravel.io

Laravel 问题的解决,知识和社区建设平台。

© 2024 Laravel.io - 所有权利保留。