주요글: 도커 시작하기
반응형

앵귤러2 기반 웹앱에 파일 업로드 기능을 추가해야 할 일이 생겨서 검색을 했더니 다음의 두 라이브러리가 얻어 걸렸다.

  • ng2-file-upload (https://github.com/valor-software/ng2-file-upload)
  • ng2-uploader (https://github.com/jkuri/ng2-uploader)

둘 다 쉽게 파일 업로드를 붙일 수 있다. 두 라이브러리를 실험해보고 나서 ng2-file-upload를 선택했다. 그 이유는 구현하려는 기능에 조금 더 맞았기 때문이다.


ng2-file-upload의 기본 사용법


데모(http://valor-software.com/ng2-file-upload/) 사이트를 보면 ng2-file-upload의 기본 사용법을 볼 수 있는데, 진짜 쉽다. ng2-file-upload가 제공하는 FileUploader 클래스와 ng2FileDrop 디렉티브와 ng2FileSelect 디렉티브를 사용하면 된다.


나 같은 경우 앵귤러2 사이트의 튜토리얼 문서에 있는 과정을 따라서 프로젝트를 생성했다. ng2-file-upload를 사용하기 위해 package.json 파일에 ng2-file-upload에 대한 의존을 추가했다.


{

  ...

  "dependencies": {

    "@angular/common": "2.0.0-rc.5",

    ...

    "bootstrap": "^3.3.6",

    "ng2-file-upload": "1.0.3"

  },

  "devDependencies": {

    "typescript": "^1.8.10",

    "typings":"^1.0.4"

  }

}


추가한 뒤에 npm install 명령어를 사용해서 ng2-file-upload를 다운로드 받았다.


그리고, 모듈 설정을 위해 systemjs를 사용했기 때문에 systemjs.config.js 파일에 ng2-file-upload에 대한 설정을 다음과 같이 추가해서 모듈 이름으로 접근할 수 있도록 했다.


(function (global) {

    // map tells the System loader where to look for things

    var map = {

        'app': '/operation-app/app-testeditor', // 'dist',

        '@angular': '/node_modules/@angular',

        'rxjs': '/node_modules/rxjs',

        'ng2-file-upload': '/node_modules/ng2-file-upload'

    };

    // packages tells the System loader how to load when no filename and/or no extension

    var packages = {

        'app': {main: 'test-editor-main.js', defaultExtension: 'js'},

        'rxjs': {defaultExtension: 'js'},

        'ng2-file-upload': {defaultExtension: 'js'}

    };

    ...

    ...

    System.config(config);

})(this);


파일 업로드 기능이 필요한 앵귤러 컴포넌트는 다음과 같이 ng2-file-upload 모듈을 임포트하면 된다.


import {FILE_UPLOAD_DIRECTIVES, FileUploader} from "ng2-file-upload/ng2-file-upload";


@Component({

    selector: 'test-file-uploader',

    templateUrl: './test-file-uploader.component.html',

    directives: [FILE_UPLOAD_DIRECTIVES]

})

export class TestFileUploader {

    public uploader:FileUploader = new FileUploader({url: '/my/upload/path');


    ...

}


FileUploader 클래스는 업로드할 파일 목록을 관리하고 파일을 전송하는 기능을 제공한다. ng2-file-upload 사이트의 데모 코드를 보면 다음과 같이 템플릿 코드에 디렉티브를 사용해서 업로드할 파일을 추가할 수 있다.


<div ng2FileDrop [uploader]="uploader" ....>

    Base drop zone

</div>


<div ng2FileDrop [uploader]="uploader" ....>

    Another drop zone

</div>


Multiple

<input type="file" ng2FileSelect [uploader]="uploader" multiple  /><br/>


Single

<input type="file" ng2FileSelect [uploader]="uploader" />


ng2FileDrop 디렉티브로 지정한 영역에 파일을 드롭하거나 ng2FileSelect를 이용해서 파일을 선택하면, 해당 파일이 [uploader] 속성으로 지정한 FileUploader 객체에 추가된다.


이렇게 FileUploader에 추가한 파일을 업로드하려면 다음과 같이 FileUplaoder의 uploadAll()을 사용하면 된다. 또는 추가한 개별 파일별로 업로드를 할 수도 있다. 데모 사이트에서 완전한 코드를 확인할 수 있다.


<button type="button" class="btn btn-success btn-s"

        (click)="uploader.uploadAll()" [disabled]="!uploader.getNotUploadedItems().length">

    <span class="glyphicon glyphicon-upload"></span> Upload all

</button>


디렉티브 없이 FileUploader 직접 사용


ng2-file-upload가 그 자체로 좋지만 내가 필요한 건 ng2-file-upload가 제공하는 파일 업로드 기능이었고, 디렉티브를 통한 연동은 필요없었다. 그래서 디렉티브를 사용해서 파일 목록을 FileUploader에 추가하지 않고 직접 추가했다.


FileUploader 생성과 파일 추가

FileUploader 객체를 앵귤러 컴포넌트 생성자에서 직접 생성했다. <input type="file"> 버튼을 눌러 선택한 파일 목록을 받기 위한 메서드는 handleUploadFileChanged()이다.


@Component( ... )

export class TestFileUploader {

    public uploader:FileUploader;


    constructor() {

        let uploadUrl = window.location.protocol + "//" + window.location.host + "/testfile/upload";

        this.uploader = new FileUploader({url: uploadUrl});

        ...

    }


    handleUploadFileChanged(event) {

        this.uploader.clearQueue();

        let files:File[] = event.target.files;

        let filteredFiles:File[] = [];

        for (var f of files) {

            if (f.name.endsWith(".pdf")) {

                filteredFiles.push(f);

            }

        }

        if (filteredFiles.length == 0) {

            this.showGuide = true;

        } else {

            this.showGuide = false;

            let options = null;

            let filters = null;

            this.uploader.addToQueue(filteredFiles, options, filters);

        }

    }

    ...


요구사항은 파일을 하나만 업로드하는 것이었기에 handleUploadFileChanged()는 먼저 현재 uploader.clearQueue())를 이용해서 uplaoder에 추가되어 있는 파일 목록을 지운다. 이를 제거하지 않으면 파일을 선택할 때마다 업로드할 파일이 추가되기 때문에, 실제 업로드를 수행할 때 마지막 선택한 파일을 포함한 이전에 선택한 모든 파일을 업로드한다.


위 코드에서는 확장자가 pdf인 파일을 걸러낸 뒤에 uploader.addToQueue()를 이용해서 업로드할 파일 목록을 직접 uploader에 추가했다.


handleUploadFileChanged()를 실행하기 위한 템플릿 코드는 다음과 같다.


<input type="file" (change)="handleUploadFileChanged($event)">



업로드와 완료 처리


업로드할 파일을 선택했다면 uploader.uploadAll()을 이용해서 선택한 파일을 업로드할 수 있다. 업로드를 완료한 뒤에 성공/실패 유무에 따라 알맞은 후속 작업(안내 문구 출력 등)을 하고 싶다면 uploader에 이벤트를 리스너를 등록하면 된다.


@Component( ... )

export class TestFileUploader {

    public uploader:FileUploader;

    private uploadResult:any = null;


    constructor() {

        let uploadUrl = window.location.protocol + "//" + window.location.host + "/testfile/upload";

        this.uploader = new FileUploader({url: uploadUrl});

        this.uploader.onSuccessItem = (item, response, status, headers) => {

            this.uploadResult = {"success": true, "item": item, "response": 

                response, "status": status, "headers": headers};

        };

        this.uploader.onErrorItem = (item, response, status, headers) => {

            this.uploadResult = {"success": false, "item": item, 

                "response": response, "status": status, "headers": headers};

        };

        this.uploader.onCompleteAll = () => {

            this.handleUploadComplete();

        };

    }


    uploadFile() {

        this.uploader.uploadAll(); // 업로드 시작

    }


    private handleUploadComplete() {

        console.log("upload compete : " + this.uploadResult.response);

        if (this.uploadResult.success) {

            ...성공 메시지 출력

        } else {

            ...실패 메시지 출력

        }

    }


FileUploader에는 onSuccessItem, onErrorItem, onCompleteAll 등의 이벤트 리스너를 등록할 수 있다. 이들 리스너를 등록하면 업로드 성공/실패 여부를 이벤트로 받아 그에 알맞은 처리를 할 수 있다. 위 코드는 한 개 파일만 업로드하므로 onSuccessItem 리스너와 onErrorItem 리스너에서 업로드 결과를 uploadResult 필드에 할당했다. 그리고, 모든 업로드 처리가 끝나면 불리는 onCompleteAll 리스너에서 업로드 성공/실패 결과에 따라 알맞은 처리를 수행하도록 했다.


+ Recent posts