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。很简单,对吧?你可能已经注意到,我们定义了两个辅助函数,validateBase64
和 storeFile
,这些函数可以验证并为我们存储文件。现在定义它们的时候到了。
第一个函数 (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 文件时重用它们。
driesvints, karimalik, ol-serdiuk 赞了这个文章