Javascript File API

  • HTML 코드
<div class="fileUpload-container">
    <div class="fileUpload">
        <label for="fileElem_1" class="btn-m btn-point marginR10">파일 선택</label>
        <input type="file" id="fileElem_1" class="fileUpload__input visually-hidden"
            accept=".gif" onchange="handleFile(this, this.files)">
        <span class="fileUpload__title">선택된 파일이 없습니다.</span>
    </div>
    <p class="fileUpload__message">
        gif/1mb 이하 등록 허용
    </p>
</div>

: 파일 인풋 태그를 그대로 사용하면 미관상 안좋아서 버튼을 따로 생성하고, <input type="file">에 .click()을 통해 파일 업로드 창을 띄우는게 일반적이다.
: 하지만 더 쉬운 방법으로, label 태그를 사용해서 click 이벤트 없이 업로드 창을 띄울 수 있다.(label의 for 속성과, input 태그의 id값을 일치시켜준다.)
: 이때, input 태그가 display : none, 이거나 visibility : hidden; 이면 동작을 안하기 때문에(왜인지 잘모름) 아래와 같이 "visually-hidden"이라는 클래스를 적용해서 안보이게 만들어준다.

: 여기서 accept="" 속성을 통해서, 선택가능한 파일의 확장자를 제한할 수 있다. 하지만, 초기에 파일업로드창에서 해당하는 확장자로 필터링하여 보여줄 뿐, 실제로 모든 파일 선택하기를 통해 다른 확장자를 추가할 수 있다.
=> 따라서 Js 코드를 통해 처리를 해줘야한다.

: onchange 이벤트 바인딩을 통해서, 선택된 파일이 변경될 경우(파일이 선택될 경우) 이벤트를 핸들링한다.
=> handleFile()의 인자로 넘어가는 this : input 태그, this.files : input 태그를 통해 접근가능한 fileList가 된다.

: multiple 속성을 주면 여러개의 파일을 선택할 수 있다.

  • this.files 내용

  • CSS 코드
.visually-hidden {
    position: absolute !important;
    height: 1px;
    width: 1px;
    overflow: hidden;
    clip: rect(1px, 1px, 1px, 1px);
}
  • Javascript 코드
  1. handleFiles()
// 파일 선택 함수
    function handleFile(self, files) {
        self = $(self);

        const $title = self.parent().find(".fileUpload__title");
        if (!$title.length) return false;

      	//1.
        if (!files.length) {
            $title[0].innerHTML = "선택된 파일이 없습니다.";
            return false;
        }

      	//2.
        if (!checkFileExt(self, files)) {
            alert("확장자를 확인해주세요");
            self[0].value = "";
            $title[0].innerHTML = "선택된 파일이 없습니다.";
            return false;
        };

        const _file = files[0];

      	//3.
        $title[0].innerHTML = _file.name.trim();
    }
  • 코드 설명
  1. 전달받은 인자 files의 length 프로퍼티를 통해서 선택된 파일이 있는지 여부를 체크한다. 선택된 파일이 없으면, $title태그의 텍스트를 변경하고, 함수를 종료한다
  2. checkFilExt()함수를 통해서 추가된 파일의 확장자가 accept 속성에 정의된 파일의 확장자와 일치하는지 여부를 판단한다.
  1. checkFileExt()
// 파일 확장자 체크 함수
    function checkFileExt(el, files) {
        //1.
        let accepts = el[0].accept; //".jpg .png .jpeg"
      
        if (!accepts) return true;

      	//2.
        accepts = accepts.split(",").map(function (ext) {
            return ext.replace(".", "").trim();
        }) //["jpg", "png", "jpeg"]

      	//3.
        var ext = files[0].name.match(/\.([^\.]+)$/)[1]; //png, jpg..

      	//4.
        if (!accepts.includes(ext)) return false;

      	//5.
        return true;
    }
  • 코드 설명
    1. 인자로 전달받은 el(input 태그)의 accept 속성에 정의된 값을 문자열 형태로 전달받는다.
    2. Array의 메소드를 통해서, accept 문자열을 배열 형태로 변환한다.
    3. 정규식을 통해서 전달받은 files의 name 프로퍼티중 확장자에 해당하는 부분을 추출한다.
    4. Array.includes() 메소드를 통해서 ext가 accepts 배열안에 포함되어 있지 않으면(허용되지 않은 확장자) false를 리턴하고 함수를 종료한다.
    5. 올바른 확장자가 들어갔으면, true를 리턴한다.

Refence


1. 정규 표현식

: 영문 대소문자, 특수문자(-), 숫자만 입력 가능하도록 처리

var regExp = /[^(a-zA-Z0-9-)]/gi;

=> "-" (하이픈)의 경우 특수문자이기때문에, (역슬래시)로 문자임을 표시한다.

const REG_EXP = {
            onlyNum: /[^0-9]/g, //숫자만 입력가능 === 숫자가 아닌 문자들 체크
            onlyEng: /[^a-zA-Z]/g, //영문 대소문자만 입력 가능 === 영어가 아닌 문자들 체크
            includeSymbol: /[^0-9a-zA-Z\-]/gi, //숫자, 영어대소문자, 특수문자 - 만 입력가능
}

[^] 는 not의 의미 => 따라서 영어 소문자/대문자 숫자, 특수문자 - 이 아닌 문자를 찾는다.

특수문자 앞에는 \를 포함해야한다.

string.match() => 정규식에 해당하는 문자들 배열 형태로 반환

string.replace(regExr, "") => 정규식에 해당하는 문자들 공백으로 변환

regExp.test(string) => 정규식에 해당하는 문자열이 있는지 여부를 boolean 값으로 반환

/regexr/i

// => 시작 종료 기호

regexr => 패턴

i => 플래그 라고한다.

2. RegExp의 메소드

2.1 exec => 찾은 문자열의 인덱스까지 반환?  ===> string.match(RegExp)와 비슷

2.2 test => true/false 반환

3. 플래그 종류

3.1 i => 대소문자 구별 없이 검색

3.2 g => 문자열 내의 모든 패턴 검색

3.3 m => 문자열의 행이 바뀌더라도 계속 검색

4. 패턴

4.1 예제

const regexr = / ... /;

여기서 .은 임의의 문자 한개를 의미한다.

const targetStr = 'AA BB Aa Bb'

targetStr.match(regexr) => "AA "가 검색됨.


+ : 앞선 패턴 반복

| : Or 의미

// 'A' 또는 'B'가 한번 이상 반복되는 문자열을 반복 검색
// 'A', 'AA', 'AAA', ... 또는 'B', 'BB', 'BBB', ...
const regexr = /A+|B+/g;

// 'html'로 끝나는지 검사
// $ : 문자열의 끝을 의미한다.
const regexr = /html$/;

5. 정규식을 이용한 글자 수, 입력 문자 제한 함수

function bindKeyupEvents() {
            const REG_EXP = {
                onlyNum: /[^0-9]/g,
                onlyEng: /[^a-zA-Z]/g,
                includeSymbol: /[^0-9a-zA-Z\-]/gi,
            }

            $(".lengthCheck").on("keyup", function (e) {
                const $titleLength = $(this).parent().find(".currentInput-length")[0];

                const dataReg = $(this).data("regExp");

                //길이 제한, 입력 제한이 있는지 확인한다.
                const MAX_LENGTH = !!this.maxLength ? this.maxLength : null;
                const regExp = dataReg ? REG_EXP[dataReg] : null;

                let _value = $(this).val();

                //regExp.test(string) => 정규식에 해당하는 문자가 있으면 true, 없으면 false를 반환한다.
                //따라서 정규식에 해당하는(입력 제한되는 문자)가 있으면 빈 공백으로 변경한다.
                if (regExp && regExp.test(_value)) {
                    _value = _value.replace(regExp, "");
                }

                //문자열이 입력 최대 길이보다 커지면, String.slice() 메소드를 이용해서 최대 길이까지 문자를 자른다.
                if (MAX_LENGTH && MAX_LENGTH < _value.length) {
                    _value = _value.slice(0, MAX_LENGTH);
                }

                $(this).val(_value);

                if ($titleLength) {
                    $titleLength.innerHTML = $(this).val().length;
                }
            })
        }

Reference :

https://dragoner.tistory.com/18

https://beans9.tistory.com/154

https://poiemaweb.com/js-regexp

profile
사람들에게 좋은 경험을 선사하고 싶은 주니어 프론트엔드 개발자

0개의 댓글