html5 api 리뷰
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.js"></script> </head> <body> <form id="fileForm"> <div> <input type="text" value="hong" name="sender" id="sender"> </div> <div> <input type="file" id="file_1"> <img id="preview" src="" alt="로컬에 있는 이미지가 보여지는 영역"> </div> <div> <button type="submit">전송</button> </div> </form> <img src="" id="image"> <script> $(document).ready(function() { $('#fileForm').on('submit', function(e) { console.log('upload'); var formData = new FormData(); console.log($("#file_1")[0].files); formData.append('sender', $('#sender').val()); formData.append('file', $("#file_1")[0].files[0], $("#file_1")[0].files[0].name); $.ajax({ url: 'http://www.javabrain.kr:8080/api/imageUpload', type: 'post', processData: false, contentType: false, data: formData, success: function(data){ console.log(data); $('#image').attr('src', 'http://www.javabrain.kr' + data.value); } }); return false; }); }); // 로컬 이미지 읽어서 썸네일 보여주기 var file = document.querySelector('#file_1'); file.onchange = function () { var fileList = file.files ; // 읽기 var reader = new FileReader(); reader.readAsDataURL(fileList [0]); //로드 한 후 reader.onload = function () { document.querySelector('#preview').src = reader.result ; }; }; </script> </body> </html> |
로컬의 이미지를 선택하면 브라우저에 썸네일을 보여주는 코드를 작성해보자.
new FileReader() 로 파일 객체를 생성한 다음 readAsDataURL(file)로 이미지를 Data Uri형태로 읽어 들인다. Data Uri는 RFC 2397에 정의되어있는 표준 프로토콜로써 이미지와 같은 데이터를 “data:image/jpg/base64,xxxx 의 형태로 된 URI로 표현하는 방식이다. 이미지 src에 이 Data Uri를 넣으면 RFC2397에 따라 구현된 브라우저에서 이미지가 표시된다.
html5 Communication API
communication API에는 websocket 과 XmlHttpRequest Level 2가 있는데, XmlHttpRequest Level2를 자세히 살펴보면 아래와 같다.
– 응답단계별 처리 이벤트 지원
– FormData 객체 지원
– upload 속성으로 손쉬운 파일업로드 지원
예전에는 enctype=”multipart/form-data” 같은 속성을 사용했지만, 이제 FormData 객체로 파일업로드를 쉽게 구현할 수 있다.
new FormData() 로 객체를 생성후에, append(key, file, filename) 형태로 formData를 추가하고, ajax 형태로 업로드를 수행한다.
multi-part 프로토콜
ajax로 파일을 전송시에 Content-Type을 multi-part 형태로 보내야 한다. multi-part 와 boundary를 만들어 주어야 하는데, 이것을 개발자가 수동으로 하기에는 어렵다. 그래서 content-type을 false로 설정하면 브라우저가 알아서 이 부분을 해준다.
processData 를 false 로 두는것은 Request body에 데이터가 x-www-url-form-encorded 형태가 아니고 formData이기 때문에 추가적인 처리를 하지 말라는 의미이다.
사진 등록 로직
사진을 등록하는 폼을 추가한다. 파일추가 버튼을 누를때 이미지만 필터링 할려면, accept=”image/* 을 추가한다.
1 2 3 4 5 6 7 |
<div class="d-flex flex-column mt-3"> <div>사진등록</div> <div> <input type="file" class="form-control-file border" (change)="fileUpload($event)"> </div> <img [src]="form.controls['photo'].value" *ngIf="form.controls['photo'].value"> </div> |
이제 추가 버튼을 누르면 fileUpload($event) 함수가 호출된다. 함수를 추가하고 로그를 찍어보자.
그러면 우리가 접근해야 할 데이터는 event.target.files라는 것을 알수 있을 것이다.
먼저 서비스에 formData를 입력으로 받아서 처리하는 post api를 추가한다. jquery에서 본것처럼 multi-part의 바운더리를 직접 만들지 않는다. 그러므로 request 헤더에 content-type을 추가하지 말아야 브라우저가 보내는 데이터가 파일일 경우 자동으로 multi-part 바운더리를 붙여준다.
1 2 3 4 5 6 7 |
imageUpload(formData: FormData): Observable<ResultVo> { let headers = new HttpHeaders(); // headers.append('Content-Type', 'multipart/form-data'); //브라우저가 자동 생성함. headers.append('Accept', 'application/json'); return this.http.post<ResultVo>(`${environment.HOST}/api/file`, formData, {headers: headers}); } |
컴포넌트에서 fileUpload 메서드를 작성한다.
먼저 photo라는 formControlName을 추가하였다. 파일을 선택하면 서버에 전송하고 전송이 성공하면 리턴값이
{result: 0, value: ‘path’} 에 담겨서 오므로 value에 있는 값을 꺼내서 photo에 넣는다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
constructor(private fb: FormBuilder, private adminService: AdminService, private toaster: ToasterService) { this.form = this.fb.group({ name: [null, Validators.compose([Validators.required, Validators.minLength(5), Validators.maxLength(20)])], email: [null, Validators.compose([Validators.required, Validators.email])], sex: [null, Validators.required], country: [null, Validators.required], address: null, power: this.fb.array(this.powers.map(x => !1)), photo: null }); } fileUpload(event: any) { console.log(event); const reader = new FileReader(); reader.readAsDataURL(event.target.files[0]); reader.onload = () => { const formData = new FormData(); formData.append('file', event.target.files[0], event.target.files[0].name); this.adminService.imageUpload(formData) .subscribe(body => { console.log(body); let image = body['value']; if (!environment.production) { image = 'http://www.javabrain.kr:3030' + image; } this.form.controls['photo'].setValue(image); }); }; } |
새로운 angular 지식은 없고 앞에서 살펴본 html5 api가 주로 사용되었다.
hero.ts에도 photo 필드를 추가한다.
1 |
photo: string; |
이제 최종적으로 사진을 추가한 hero 등록을 해보자.