Maintenant qu’on peut afficher du contenu aux utilisateurs grâce à Workshop 4 : Création d’un page de contenu authentifiée, on peut créer des formulaires pour modifier des données, entre autre pour l’upload d’image.
La manière la plus simple de téléverser (upload) une image est de l’envoyer seule en POST dans un formulaire de type Content-Type: multipart/form-data;
. C’est la manière la plus simple d’envoyer du contenu binaire (fichier),
Il faut donc que l’objet qui contient de l’image soit déjà créé, imaginons :
Synthesizer{id=1, brand=Roland, name=Juno-106, filename=null}
filename
)Côté serveur, l’image est stockée sur disque (pas en BDD), et la BDD a un lien vers l’image. L’objet en base est maintenant Synthesizer{id=1, brand=Roland, name=Juno-106, filename=1984-JUNO-106.jpeg}
.
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
@Column
private String filename;
@PostMapping("/api/synths/{id}/image")
public void createSynthImage(@PathVariable("id") long id, @RequestParam("file") MultipartFile file) throws Exception {
Synthesizer synthesizer = synthesizerRepository.findById(id).orElseThrow();
// The "public" directory is automatically used by Spring to serve static assets
Path publicDirectory = Paths.get(".", "public").toAbsolutePath();
byte[] imageContent = file.getBytes();
Path filepath = Paths.get(publicDirectory.toString(), file.getOriginalFilename());
try (OutputStream os = Files.newOutputStream(filepath)) {
os.write(imageContent);
}
synthesizer.setFilename(file.getOriginalFilename());
synthesizerRepository.save(synthesizer);
}
MultipartFile file
, géré par Spring, et contient l’image envoyé dans le formulairefilename
de l’objet et on persiste en BDDImage stockée sur disque
<td>
<img *ngIf="synth.filename" src="http://127.0.0.1:8080/" style="width: 250px"/>
<form [formGroup]="uploadForm" (ngSubmit)="onSubmit($event)">
<div>
<input type="file" name="profile" (change)="onFileSelect($event)" data-id="" />
</div>
<div>
<button type="submit" class="btn btn-primary">Upload</button>
</div>
</form>
</td>
import {Component, OnInit} from '@angular/core';
import {HttpClient, HttpHeaders} from "@angular/common/http";
import {Router} from "@angular/router";
import {FormBuilder, FormGroup} from '@angular/forms';
@Component({
selector: 'app-synths',
templateUrl: './synths.component.html',
styleUrls: ['./synths.component.css']
})
export class SynthsComponent implements OnInit {
synths: any;
uploadForm: FormGroup;
constructor(private http: HttpClient,
private router: Router,
private formBuilder: FormBuilder) {
}
ngOnInit(): void {
// Initialise le composant synth
this.http
.get("http://127.0.0.1:8080/api/synths", SynthsComponent.getAuthenticatedHttpOptions())
.subscribe(
synths => this.synths = synths,
error => alert(`You need to be logged in to see this page: ${error}`)
)
// Initialise le composant form
this.uploadForm = this.formBuilder.group({profile: ['']});
}
onFileSelect(event) {
// Mettre le contenu de l'image dans le composant
// "uploadForm" (pour afficher le nom du fichier
if (event.target.files.length > 0) {
const file = event.target.files[0];
this.uploadForm.get('profile').setValue(file);
}
}
onSubmit(event) {
// Envoi du formulaire, on ajoute au formulaire le
// contenu du fichier
const synthId = event.target[0].attributes.id.value
const formData = new FormData();
formData.append('file', this.uploadForm.get('profile').value);
this.http
.post<any>(
`http://127.0.0.1:8080/api/synths/${synthId}/image`,
formData,
SynthsComponent.getAuthenticatedHttpOptions())
.subscribe(
() => this.ngOnInit(),
error => alert(`Error: ${error}`)
);
}
private static getAuthenticatedHttpOptions(): any {
const token = sessionStorage.getItem('token');
if (token) {
return {
headers: new HttpHeaders({
'Authorization': `Basic ${token}`,
})
};
}
return undefined;
}
}
event.target[0].attributes.id.value
(il y a probablement une meilleure manière de faire)formData.append('file', this.uploadForm.get('profile').value);
, qui sera envoyé dans le POST. Automatiquement le header suivant s’ajoute qui est important aussi : Content-Type: multipart/form-data;
. Autrement dit, on n’envoi pas le contenu en JSON.this.ngOnInit()
Maintenant que nous avons une super application, nous allons la déployer, voir Workshop 6 : Déployer son chef d’oeuvre.