angular - 从离子角度上传文件时出错。但它通过 Postman Laravel API 工作
问题描述
使用 Laravel API 在 Ionic Angular 项目中上传文件时出现错误。通过 Postman 调用时上传工作正常,但在 Ionic 中却不行。
错误: “http://localhost:8000/api/upload-media 的 Http 失败响应:400 错误请求”
- 0:“文件必须是文件。”
- 1:“文件必须是以下类型的文件:jpeg、png、jpg、svg、mp4、txt、pdf、doc。”
离子:上传-test.page.html
<ion-header>
<ion-toolbar>
<ion-title>Upload File Test Page</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<form novalidate="novalidate" [formGroup]="myForm" enctype="multipart/form-data">
<div class="px-4 py-6">
<label class="block text-sm">
<span class="text-gray-700 dark:text-gray-400 font-semibold required">File</span>
<div class="relative text-gray-500 focus-within:text-purple-600 dark:focus-within:text-purple-400">
<input class="block w-full pl-10 mt-1 text-sm text-black dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-input" placeholder="File ..." type="file" formControlName="file"/>
<div class="absolute inset-y-0 flex items-center ml-3 pointer-events-none">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 13h6m-3-3v6m5 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
</div>
</div>
<span class="text-xs m-2 text-red-600 dark:text-red-400" *ngIf="myForm.controls.name && myForm.controls.name.touched && myForm.controls.name.status=='INVALID'">Error on this field.</span>
</label>
<button class="block w-full px-4 py-2 mt-6 mb-4 text-sm font-medium leading-5 text-center text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-lg active:bg-purple-600 hover:bg-purple-700 focus:outline-none focus:shadow-outline-purple disabled:bg-purple-400" (click)="onClickUpload()">
<svg xmlns="http://www.w3.org/2000/svg" class="inline-block align-middle w-4 mr-2 ml-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
</svg>
<span>Upload</span>
</button>
</div>
</form>
</ion-content>
离子:上传-test.page.ts
/* eslint-disable eqeqeq */
/* eslint-disable @typescript-eslint/dot-notation */
/* eslint-disable prefer-const */
/* eslint-disable @typescript-eslint/naming-convention */
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { MediaGalleryService } from '../platform/services/api/media-gallery.service';
import { ToastService } from '../platform/services/utils/toast.service';
@Component({
selector: 'app-upload-test',
templateUrl: './upload-test.page.html',
styleUrls: ['./upload-test.page.scss'],
})
export class UploadTestPage implements OnInit {
myForm: FormGroup;
constructor(private formBuilder: FormBuilder, private mediaGalleryService: MediaGalleryService, private toastService: ToastService) {
/* Form builder */
this.myForm = this.formBuilder.group({
file: new FormControl('', Validators.compose([
Validators.required,
])),
advertisement_id: new FormControl(1, Validators.compose([
Validators.required,
])),
type: new FormControl('ad_video', Validators.compose([
Validators.required,
])),
folder: new FormControl('ad_video', Validators.compose([
Validators.required,
])),
extension: new FormControl('', Validators.compose([
Validators.required, Validators.pattern('^(jpeg|png|jpg|svg|mp4|txt|pdf|doc|docx)$'),
]))
},{
validators: []
});
}
ngOnInit() {
}
async onClickUpload() {
console.log(this.myForm.value.file);
const fileExtension = this.myForm.value.file.split(['.'])[(this.myForm.value.file.split(['.']).length - 1)];
this.myForm.controls['extension'].setValue(fileExtension);
//
console.log(this.myForm);
//
if(this.myForm.status == 'VALID') {
this.mediaGalleryService.save(this.myForm.value).subscribe(
(response) => {
console.log(response);
if(response?.success){
/* Toast */
this.toastService.createToast('File Uploaded With Success.', 2000);
}else{
/* Toast */
this.toastService.createToast('Error : '+response.message, 2000);
}
},
(error) => {
console.log(error);
/* Toast */
this.toastService.createToast('Error : '+error?.message, 2000);
}
);
}
}
}
离子:上传-test.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { IonicModule } from '@ionic/angular';
import { UploadTestPageRoutingModule } from './upload-test-routing.module';
import { UploadTestPage } from './upload-test.page';
@NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
UploadTestPageRoutingModule,
ReactiveFormsModule,
],
declarations: [UploadTestPage]
})
export class UploadTestPageModule {}
离子:媒体画廊.service.ts
/* eslint-disable quote-props */
/* eslint-disable @typescript-eslint/ban-types */
/* eslint-disable prefer-const */
/* eslint-disable @typescript-eslint/naming-convention */
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { environment } from 'src/environments/environment';
@Injectable({
providedIn: 'root'
})
export class MediaGalleryService {
constructor(private http: HttpClient) { }
save(data) {
let headersObj = new HttpHeaders();
headersObj.append('Content-Type', 'multipart/form-data');
headersObj.append('Accept', 'application/json');
let options = { headers: headersObj };
//
return this.http.post<any>(environment.apiURL+'/upload-media', data, options);
}
destroy(id,mediaType) {
return this.http.delete<any>(environment.apiURL+'/medias/'+id+'/'+mediaType);
}
}
IONIC:身份验证-request.interceptor.ts
/* eslint-disable max-len */
/* eslint-disable space-before-function-paren */
import { HttpEvent, HttpHandler, HttpRequest, HTTP_INTERCEPTORS } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { from, Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
import { StorageService } from './storage.service';
@Injectable({
providedIn: 'root'
})
export class AuthenticationRequestInterceptor {
constructor(private storageService: StorageService) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return from(this.handle(request, next));
}
async handle(request: HttpRequest<any>, next: HttpHandler) {
let authRequest = request;
let token = null;
await this.storageService.get(environment.authenticatedUserTokenKey).then(async(receivedToken)=>{await (token = receivedToken);});
const TOKEN_HEADER_KEY = 'Authorization';
const CONTENT_TYPE_HEADER_KEY = 'Content-Type';
const ACCEPT_HEADER_KEY = 'Accept';
//
const ACCESS_CONTROL_ALLOW_ORIGIN_KEY = 'Access-Control-Allow-Origin';
const ACCESS_CONTROL_ALLOW_METHODS_KEY = 'Access-Control-Allow-Methods';
const ACCESS_CONTROL_ALLOW_HEADERS_KEY = 'Access-Control-Allow-Headers';
//
if (token) {
authRequest = authRequest.clone({ headers: authRequest.headers.set(TOKEN_HEADER_KEY, 'Bearer ' + token) });
}
if (!authRequest.headers.has('Content-Type')) {
authRequest = authRequest.clone({ headers: authRequest.headers.set(CONTENT_TYPE_HEADER_KEY, 'application/json') });
}
authRequest = authRequest.clone({ headers: authRequest.headers.set(ACCEPT_HEADER_KEY, 'application/json') }); //text/plain
//
authRequest = authRequest.clone({ headers: authRequest.headers.set(ACCESS_CONTROL_ALLOW_ORIGIN_KEY, '*') });
authRequest = authRequest.clone({ headers: authRequest.headers.set(ACCESS_CONTROL_ALLOW_METHODS_KEY, 'GET, POST, PUT, DELETE, OPTIONS, PATCH, TRACE, CONNECT, HEAD') });
authRequest = authRequest.clone({ headers: authRequest.headers.set(ACCESS_CONTROL_ALLOW_HEADERS_KEY, '*') });
//
return next.handle(authRequest).toPromise();
}
}
export const authenticationInterceptorProviders = [
{ provide: HTTP_INTERCEPTORS, useClass: AuthenticationRequestInterceptor, multi: true }
];
LARAVEL:MediaGalleryController.php
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\MediaGallery;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\str;
class MediaGalleryController extends Controller {
/* Store a newly created resource in storage. */
public function store(Request $request) {
// Check current authenticated user privileges, if he could execute the following operations.
$authorized = false;
foreach(Auth::user()->role->functionalities as $functionality) {
if($functionality->slug == 'manage_my_files' || $functionality->slug == 'manage_all_files'){
$authorized = true;
break;
}
}
if(!$authorized) {
return response()->json(["message"=>"You are not authorized.", "data"=>[], "errors"=>["Unauthorized"], "success"=>false], 401);
}
// Validate request.
$mediaGalleryValidationRules = [
'type' => ['required', 'in:ad_video,ad_image,instruction_file'],
'folder' => ['required', 'in:ad_video,ad_image,instruction_file'],
'file' => ['required', 'file', 'mimes:jpeg,png,jpg,svg,mp4,txt,pdf,doc', 'max:20480'],
'extension' => ['required', 'string', 'in:jpeg,png,jpg,svg,mp4,txt,pdf,doc'],
'advertisement_id' => ['required_if:type,==,ad_video,||,type,==,ad_image,||,type,==,instruction_file', 'numeric', 'exists:advertisements,id'],
];
$mediaGalleryValidator = validator($request->all(), $mediaGalleryValidationRules);
if($mediaGalleryValidator->fails()) {
return response()->json(["message"=>"Errors on uploading this media to gallery.", "data"=>$request->all(), "errors"=>$mediaGalleryValidator->errors(), "success"=>false], 400);
}
// Generate a file name.
$mediaPath = 'public/medias/' . strtolower($request->get('folder')) . '/';
$mediaExtension = '.' . $request->get('extension');
$counter = 0;
do{
$counter = $counter + 1;
$mediaName = date('YmdHis') . '-' . Str::uuid()->toString();
}while(Storage::disk('public')->exists($mediaPath . $mediaName . $mediaExtension) || $counter<=5);
// Upload file.
if($mediaPath && $mediaName && $mediaExtension) {
Storage::disk('public')->put($mediaPath . $mediaName . $mediaExtension, file_get_contents($request->file('file')->getRealPath()));
$fileName = '/storage/public/medias/' . strtolower($request->get('folder')) . '/' . $mediaName . $mediaExtension;
}
// Save data.
$mediaGallery = new MediaGallery([
'type' => $request->get('type'),
'value' => $fileName,
//
'created_by' => Auth::user()->id,
'updated_by' => Auth::user()->id,
]);
//
if($request->get('type') == 'ad_video' || $request->get('type') == 'ad_image' || $request->get('type') == 'instruction_file'){
$mediaGallery->advertisement_id = $request->get('advertisement_id');
}
//
$mediaGallery->save();
// Return result.
return response()->json(["message"=>"Media uploaded successfully.", "data"=>$mediaGallery, "errors"=>[], "success"=>true], 201);
}
}
我希望尽快得到解决方案。非常感谢。
解决方案
推荐阅读
- google-apps-script - 选择特定工作表时触发宏 - Google 表格
- c++ - /usr/bin/ld: 输入文件 `func.o' 的 i386 架构与 i386:x86-64 输出不兼容
- sql - 选择 Postgres 数组列的 JSON 操作?
- python - tkinter - 无法隐藏嵌套的顶层窗口
- php - 通过 WooCommerce rest api 创建产品时设置产品标签 ID
- c# - 我可以在方法中检索参数的默认值吗?
- ironsource - ironSource - 如何在现有应用程序中激活横幅广告单元
- python - 如何将一个函数向量化为两个,将两个不同长度的序列(m 和 n)作为参数,并在 Python NumPy 中返回一个矩阵(mxn)?
- python-3.x - xml rpc 客户端和服务器
- python - Django过滤多对多关系