简体   繁体   中英

How to Pass Data from Angular 10 form to Spring Boot Rest API that includes both Strings and Files/Images?

I am getting the following error in Eclipse Console when trying to communicate between Angular 10 and Spring-Boot rest Api.

2020-10-06 23:44:12.261  WARN 33762 --- [nio-8090-exec-5] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token
 at [Source: (PushbackInputStream); line: 1, column: 535] (through reference chain: com.P.models.PModels["pProfilePic"])]

This is part of my Angular Form

<form class="" action="" method="POST" enctype = "multipart/form-data" role="form" [formGroup]="form" (ngSubmit)="onSubmit()">

                  <input id="name" class="form-control" type="text" placeholder="Your Name" formControlName="pName"
                    class="form-control" [ngClass]="{ 'is-invalid': submitted && f.pName.errors }">
                  <div *ngIf="submitted || f.pName.invalid && (f.pName.dirty || f.pName.touched)"
                    class="alert alert-danger">
                    <div *ngIf="f.pName.errors?.required">Name is required</div>
                  </div>

<div class="form-group row mb-4">
            <label for="uploadPhoto" class="col-md-3 col-form-label">Upload Photo </label>
            <div class="col-md-8">
              <input type="text" class="form-control mb-2 transparent-disabled" id="uploadPhoto"
                placeholder="JPEG size 200kb max" [value]="filePhotoInfo" disabled>
               <div *ngIf="submitted || f.pProfilePhoto.invalid && (f.pProfilePhoto.dirty || f.pProfilePhoto.touched)"
                class="alert alert-danger">
                <div *ngIf="f.pProfilePhoto.errors?.required"> Upload JPEG file and it should not exceed 200kb</div>
              </div> 
              <button type="button" class="btn btn-outline-primary btn-upload"><i class="fas fa fa-cloud-upload"></i>
                Upload File

                <input
                      formControlName="pProfilePhoto" 
                      type="file" 
                      (change)="onPhotoSelect($event)"
                      class="form-control" 
                      >
              </button>
            </div>
          </div>

This is part of my Registration ts file

import { Component, OnInit } from '@angular/core';
import { NgbModalConfig, NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { FormBuilder, FormControl, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { ValidationService } from 'src/app/Services/validation.service';
import { PRegistrationService } from 'src/app/Services/P-Services/p-registration.service';
@Component({
  selector: 'app-p-registration',
  templateUrl: './p-registration.component.html',
  styleUrls: ['./p-registration.component.css', '../../../app.component.css'],
  providers: [NgbModalConfig, NgbModal]

})


export class PRegistrationComponent implements OnInit {
  checkbox1 = false;
  form: any;
  loading = false;
  submitted = false;
  public fileData = {};
   constructor(
    private PRegistrationService : PRegistrationService,
    config: NgbModalConfig,
    private modalService: NgbModal,
    private formBuilder: FormBuilder,
    private route: ActivatedRoute,
    private router: Router,
    private customValidator: ValidationService,
    ) {
    // customize default values of modals used by this component tree
    config.backdrop = 'static';
    config.keyboard = false;
}

ngOnInit() {
  this.form = this.formBuilder.group({
    pName: ['', Validators.required],
      pProfilePhoto: ['', Validators.required],
      pProfilePic: new FormControl(''),
  });
}
// convenience getter for easy access to form fields
  get f() { return this.form.controls; }

  // tslint:disable-next-line: typedef
  onSubmit() {
    this.submitted = true;
    if (this.form.valid) {
      const formData = new FormData();
      formData.append('pProfilePic', this.form.get('pProfilePic').value);
      this.PRegistrationService.pRegistration(this.form.value)
        .subscribe(result => {
          this.router.navigate(['PRegistration']);
        }, (err) => {
          console.log(err);
        });
    }
  }
  // tslint:disable-next-line: typedef
  reset(){
    this.form.reset();
  }
  // tslint:disable-next-line: typedef
  open(content) {
    this.modalService.open(content, { centered: true });
  }


  filePhotoInfo: string ='';

  onPhotoSelect(event) {
  
    if (event.target.files.length > 0) {
      const pProfilePhoto = event.target.files[0];
      console.log('this is ')
      console.log(pProfilePhoto);
      this.filePhotoInfo = `${pProfilePhoto.name}`;
      this.form.patchValue({
        pProfilePic: pProfilePhoto
      });
    }
  }
}

This is part of my service ts file

export class PRegistration {
    public pId: number;
    public pName: string;
    public pProfilePic: File;

    constructor(    
        pId: number,
        pName: string,
        pProfilePic: File) 
        
        {
        this.pId = pId;
        this.pName = pName;
        this.pProfilePic = pProfilePic;
    }    

}

This is part of my Service file

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import {HttpClient,HttpHeaders} from "@angular/common/http";
import { environment } from 'src/environments/environment';
import { PRegistration } from 'src/app/Services/P-Services/p-registration';

@Injectable({
  providedIn: 'root'
})
export class PRegistrationService {
  constructor(private http: HttpClient) { }
  httpOptions = {
    headers: new HttpHeaders({
      'Content-Type':  'application/json',
    })
  }
  pRegistration(p):Observable<any>{

    return this.http.post<PRegistration>(environment.api_url+"save_user",p,this.httpOptions)
  
  }
}

This is part of my model

@Entity
@Table(name = "prepository")
public class PModels {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int pId;
@Column(nullable = false)
private String pName;
private String pProfilePic;

// Constructor, getter and setter, toString

This is part of my service

public void saveUser(String pName,MultipartFile pProfilePic) {
    try {
        String pProfilePic_file = pProfilePic.getOriginalFilename();
        
        InputStream pProfilePic_is = pProfilePic.getInputStream();
        
        String pProfilePic_ts = String.valueOf(System.currentTimeMillis());
        try { Thread.sleep(1);} catch (InterruptedException e) { e.printStackTrace();}
        
        String rand = UUID.randomUUID().toString();
        
        String pProfilePic_name = pProfilePic_ts+rand+pProfilePic_file;
        
    PModels p = new PModels();
    
    p.setpName(pName);
    p.setpProfilePic(pProfilePic_name);

    pDao.save(p);
    
    String folder = "//Users//Desktop//p//";
    Path path = Paths.get(folder);
    if (!Files.exists(path)) {
        try {
            Files.createDirectory(path);

            }
        catch (IOException e) {
            e.printStackTrace();
            }
        System.out.println("Directory created");
        }
    else {
        System.out.println("Directory already exists");
        }
    folder = folder + p.getPId() + "//";
    Path path1 = Paths.get(folder);
    if (!Files.exists(path1)) {
        try {
            Files.createDirectory(path1);
        }
        catch (IOException e) {
            e.printStackTrace();
            }
        System.out.println("Directory created");
        }
    else {
        System.out.println("Directory already exists");
        }
      Files.copy(pProfilePic_is,Paths.get(folder+pProfilePic_name),StandardCopyOption.REPLACE_EXISTING);
  
  
} catch (IOException e) {
    e.printStackTrace();
}
    
}

This is part of my controller

@PostMapping("/save_user")
     
    
      public void saveUser(@RequestBody PModels pModels) {
      this.pInfo.saveUser(pModels);
      
      System.out.println(pModels.getpName()); // Just for checking

In you case, the Spring Framework assumes that the incoming data is encoded as application/json but it is indeed encoded as multiparty/form-data. First of all, you need to set the ‚consumes' attribute of the @PostMapping annotation. Then you need to use the @RequestPart annotation instead of the @RequestBody one.

I think this blog post shows how to do it.

If I may add another hint: Usually, it's not a good idea to have the same POJO (class) representing both, the DB entity and the DTO for the web service.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM