用Laravel在RESTFul API中处理Angular Base64图像
在Web应用程序中处理图像已经成为一种常态。几乎99%的应用程序,我们每天都会以这样或那样的方式与之互动,有图像。
然而,处理后端图像已经被证明是一个非常复杂的任务。出于这个原因,开发人员想出了一些处理图像的替代方法。
本教程将介绍如何从Angular应用程序中以base64格式上传图片,并以图片的形式上传到服务器。
前提条件
要跟随本教程,你需要具备以下条件。
- 本地安装了PHP 7.3+。
- 安装了Laravel 8。
- 安装了Angular 12。
- 在本地安装SQL和MySQL的基本知识。
- 一个你感兴趣的IDE。在本文中,我们将同时使用PhpStorm for PHP和Webstorm for Angular。
在本教程结束时,读者应该知道如何在Angular和Laravel中处理base64图片。
设置Angular应用程序
有不同的方法来设置Angular应用程序。然而,本文使用Angular CLI来安装我们的应用程序。
键入以下内容来检查你当前安装的版本。
# command to check the current ng version
ng --version
...
# My installed version CLI (this may differ from your version)
Angular CLI: 12.2.3
# My current Node version
Node: 16.5.0
# My current npm version
Package Manager: npm 7.19.1
# On ubuntu 20.04
OS: linux x64
...
注意,上面的输出可能有所不同,不一定符合你的版本
接下来,继续并通过运行以下命令安装Angular应用程序。
ng new base64 # this installs a new angular application
根据你的网络连接情况,上述命令可能需要一些时间来执行。
安装完成后,导航到项目根目录,并创建一个图像组件,如下所示。
cd base64
ng g c image
上述命令生成了四个文件,包括模板和TypeScript文件。
现在我们有了图像组件,编辑app.component.html 文件,如下图所示。
<app-image></app-image>
上面的标签确保图像组件在执行AppComponent 时得到显示。
让我们改变我们的ImageComponent 模板,如下图所示。
<div class="content mat-elevation-z8">
<h2 class="text-center text-dark">Upload Image</h2>
<form [formGroup]="uploadForm" (ngSubmit)="onSubmit()">
<div class="form-row m-3">
<div class="col-md-6">
<label for="news_banner">Image</label>
<input type="file" id="file" (change)="handleImageUpload($event)" class="form-control-file"required="">
</div>
</div>
</form>
</div>
你注意到我们在上面的模板中添加了一个onchange 事件处理程序。这保证了只有在每次添加新图片时才会上传图片。
接下来,编辑ImageComponent 脚本文件,如下图所示。
...
import {ApiService} from "../../../core/services/api.service";
import {ToastrService} from "ngx-toastr";
import * as _ from 'lodash';
...
export class ImageComponent implements OnInit {
//image
uploadImageBase64: string;
...
/**
* on image submit
*/
onSubmit(){
let upload:uploads={
banner_image: this.uploadImageBase64,
}
this.apiService.uploadImage(news)
.subscribe((res)=>{
if(res){
// if response is successful
this.toastrService.success(res.message);
this.submitting=false;
}
else{
// if the response fails, show error alert
this.toastrService.error(res.message,'Failed');
this.submitting=false;
}
},error => {
//if an error occurs during the api request
this.toastrService.error(error.error.message,'Error');
this.submitting=false;
})
}
/**
* handle image upload
* @param fileToUpload
*/
// @ts-ignore
handleImageUpload(fileToUpload) {
// check for image to upload
// this checks if the user has uploaded any file
if (fileToUpload.target.files && fileToUpload.target.files[0]) {
// calculate your image sizes allowed for upload
const max_size = 20971510;
// the only MIME types allowed
const allowed_types = ['image/png', 'image/jpeg','image/jpg'];
// max image height allowed
const max_height = 14200;
//max image width allowed
const max_width = 15600;
// check the file uploaded by the user
if (fileToUpload.target.files[0].size > max_size) {
//show error
this.error = 'max image size allowed is ' + max_size / 1000 + 'Mb';
//show an error alert using the Toastr service.
this.toastrService.error(this.imageError,'Error');
return false;
}
// check for allowable types
if (!_.includes(allowed_types, fileInput.target.files[0].type)) {
// define the error message due to wrong MIME type
let error = 'The allowed images are: ( JPEG | JPG | PNG )';
// show an error alert for MIME
this.toastrService.error(error,'Error');
//return false since the MIME type is wrong
return false;
}
// define a file reader constant
const reader = new FileReader();
// read the file on load
reader.onload = (e: any) => {
// create an instance of the Image()
const image = new Image();
// get the image source
image.src = e.target.result;
// @ts-ignore
image.onload = rs => {
// get the image height read
const img_height = rs.currentTarget['height'];
// get the image width read
const img_width = rs.currentTarget['width'];
// check if the dimensions meet the required height and width
if (img_height > max_height && img_width > max_width) {
// throw error due to unmatched dimensions
error =
'Maximum dimensions allowed: ' +
max_height +
'*' +
max_width +
'px';
return false;
} else {
// otherise get the base64 image
this.uploadedImageBase64 = e.target.result;
}
};
};
// reader as data url
reader.readAsDataURL(fileToUpload.target.files[0]);
}
}
}
我们在上面的脚本中设置了onSubmit() 方法。这个方法是用来提交上传的base64图片的。
handleImageUpload() 方法是我们的图像处理程序。它首先检查被上传的图像大小。如果图片的尺寸符合我们预定的尺寸,我们就上传它。
接下来,我们检查图像是否只包含所需的MIME,即JPG、PNG和JPEG。当我们的检查完成后,我们使用FileReader 方法处理我们的图像。
为图片上传设置服务器
现在,我们已经有了Angular应用程序并运行,让我们来设置我们的Laravel 8服务器来处理图像。
首先, 通过编辑.env 文件来设置数据库的配置,如下图所示。
...
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=image
DB_USERNAME=yourDBusername
DB_PASSWORD=yourDBpassword
接下来, 通过运行以下命令来创建Image 模型:
php artisan make:model Image --m
上述命令在App/Models 文件夹中生成了一个模型和一个迁移文件.
现在,编辑该模型和迁移文件,如下图所示。
<?php
//edit model as shown below
namespace App\Models;
class Image extends Model
{
....
protected $fillable=[
'image_path',
];
...
}
上面的脚本创建了一个image_path 的模型,我们以后会用它来存储我们上传的文件的路径。
<?php
...
class CreateImageTable extends Migration
{
public function up()
{
Schema::create('images', function (Blueprint $table) {
$table->id();
$table->string('image_path')->nullable();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('images');
}
}
上面的脚本创建了我们的数据库表,用来存储我们上传的文件细节。
接下来,执行下面的命令来迁移我们的数据库。
php artisan migrate
随着数据库和模型设置的完成,让我们创建一个控制器来处理Angular应用程序中的图像。
为了处理这个问题,首先在App/Repos 命名空间中创建一个图像库,并按以下方式更新。
<?php
namespace App\Repos;
...
use Intervention\Image\Facades\Image;
...
class ImageRepository
{
// define a method to upload our image
public function upload_image($base64_image,$image_path){
//The base64 encoded image data
$image_64 = $base64_image;
// exploed the image to get the extension
$extension = explode(';base64',$image_64);
//from the first element
$extension = explode('/',$extension[0]);
// from the 2nd element
$extension = $extension[1];
$replace = substr($image_64, 0, strpos($image_64, ',')+1);
// finding the substring from
// replace here for example in our case: data:image/png;base64,
$image = str_replace($replace, '', $image_64);
// replace
$image = str_replace(' ', '+', $image);
// set the image name using the time and a random string plus
// an extension
$imageName = time().'_'.Str::random(20).'.'.$extension;
// save the image in the image path we passed from the
// function parameter.
Storage::disk('public')->put($image_path.'/' .$imageName, base64_decode($image));
// return the image path and feed to the function that requests it
return $image_path.'/'.$imageName;
}
}
在上面的脚本中,我们已经定义了ImageRepository 类。该类有一个upload_image() 方法,该方法需要两个参数,即从前端提交的base64图片和image_path。
上述方法中传递的图像路径将是我们存储图像的地方。
该函数也有几个变量。
$image_64持有base64格式的图像。$extension指的是图片的扩展名,即png。$imageName.我们使用PHP内置的方法time()和一个随机的字符串串联在一起得出图片的名字。这确保了图像名称的唯一性。- 返回语句返回图片的路径。
接下来,让我们来创建我们的控制器。运行以下命令,在App/Http/Controllers 目录下创建一个控制器。
php artisan make:controller ImageUploaderController
打开这个新文件App/Http/Controllers/ImageUploaderController.php ,并进行如下更新。
<?php
/**
* @group Image
* Create new images uploaded
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function createUploadedImages(Request $request) {
// validate the incoming API requests to ensure
// that they contain the images to upload
$validator=Validator::make($request->all(),[
'image' =>'required',
]);
// if the request does not contain the image
// handle the error message as shown below
if($validator->fails()){
return response()->json([
'success'=>false,
'message'=>$validator->errors()->first(),
],400);
}
// otherwise the image has been received and it's time handle them
$attachment_url= (new ImageRepository)
->upload_image($request->input('banner_image'),'test-images');
//use Image model to create image
$image=Image::create([
'image' =>$attachment_url
]);
// return the success response
return response()->json([
'success'=>true,
'message'=>"You have successfully created a test image",
],201);
}
在上面的控制器中,我们定义了createUploadedImages 方法来处理请求。然后,我们继续验证这个请求并处理图像。
我们调用了图像库类来处理我们的图像。然后我们使用图像类实例在数据库表中创建一个图像。
最后,返回语句向上传应用程序发送一个成功信息,在这种情况下,Angular前端。
测试
为了测试我们的应用程序,我们需要定义我们的路由,并确保所有提交给服务器的图片是base64的。
因此,编辑routes/api.php 文件,如下图所示。
...
Route::post('image-uploader',[ImageUploaderController::class,'createUploadedImages']);
...
接下来,通过运行以下命令为应用程序提供服务。
php artisan serve
现在你已经在你的Angular应用程序中运行了API服务器,编辑你最初创建的api服务,如下所示。
....
/**
* Upload new images
*/
uploadImage(image:Image):Observable<Image>{
return this.httpClient.post<Image>(`${environment.API_BASE_URL}/image-uploader`,image);
}
...
上述api服务确保我们只向我们在API服务器路由中定义的路由提交图片。
就这样,你现在可以使用Angular和Laravel轻松地处理图片了。
总结
在这篇文章中,我们已经深入地讨论了图片上传。我们还看到,我们如何使用事件将图像转换为base64。
我们也看到了我们如何使用图像库来处理base64图像,并将图像路径保存到服务器上的数据库中。