내가 한 노력들

Codeigniter에서 TinyEditor사용하여 PDF 파일 저장하기 본문

IT 공부/CodeIgniter

Codeigniter에서 TinyEditor사용하여 PDF 파일 저장하기

JONGI-N CHOI 2022. 6. 7. 22:27

환경

Codeigniter 버전 2

TinyEditor   버전 5

 

TinyEditor 란?

텍스트 편집기 라이브러리입니다. 

텍스트 편집에 관한 많은 플러그인을 제공해주기 때문에, 많은 기능을 쉽고 빠르게 구현할 수 있습니다. 

 

TinyEditor 사용방법

CDN으로, 라이브러리 컨텐츠 받아오기 

<script src="https://cdn.tiny.cloud/1/{API}/tinymce/6/tinymce.min.js" referrerpolicy="origin"></script>

CDN을 이용해서, 컨텐츠를 정상적으로 받아오기 위해서는 API가 필요합니다. 

API는 TinyEditor홈페이지 무료 가입을하면 받을 수 있습니다. 

 

<textarea id="myTextarea">
 	Welcome to TinyMCE!
</textarea>
<script>
tinymce.init({
  selector: '#myTextarea',
  plugins: 'a11ychecker advcode casechange export formatpainter image editimage linkchecker autolink lists checklist media mediaembed pageembed permanentpen powerpaste table advtable tableofcontents tinycomments tinymcespellchecker',
  toolbar: 'a11ycheck addcomment showcomments casechange checklist code export formatpainter image editimage pageembed permanentpen table tableofcontents',
  toolbar_mode: 'floating',
  tinycomments_mode: 'embedded',
  tinycomments_author: 'Author name',
});
</script>

Textarea를 사용하고자 하는 곳에, 위의 script문을 추가합니다. 

selector는 대상이되는 Textarea를 지정

plugins는 TinyEditor에서 제공하는 다양한 플러인중에서 사용하고자 하는 플러그인을 추가하면 됩니다. 

 

 

TinyEditor에서 file저장을 하기 위해서, script문에 file 저장 관련 코드를 추가해야합니다. 

우선 플러그인에는 "link"플러그인이 필요하며, 

file_picker_types: 'file', // e.g. "file,image,media"
file_picker_callback: function (callback, value, meta) {
    if (meta.filetype === 'file') { // anchor link
        uploadFileHandler(callback, value, meta);
    }
}

저장할 파일의 타입을 정해줄 수 있는데, 이번에는 PDF파일을 저장하기 위해서 file을 선언 , 

그리고 파일을 선택했을 경우에 실행될 핸들러 함수를 선언해줍니다. 

 

file 저장 핸들러 함수

function uploadFileHandler(callback, value, meta)
{
  let allowedFileTypes = 'application/pdf';
  let input = document.createElement('input');

  input.setAttribute('type', 'file');
  input.setAttribute('name', 'file');
  input.setAttribute('accept', allowedFileTypes);
  input.click();

  input.onchange = function() {
    let file = this.files[0];

    // API側(CodeIgniter側)に送るデータを準備
    let postData = new FormData();
    // postData.append('file', file, file.name);
    // PDFデータを送る
    postData.append('body_pdf', file, file.name); // $_FILES['body_pdf'] => array

    let filesUploadUrl = test_url;

    let xhr = new XMLHttpRequest();
    
    xhr.open('POST', filesUploadUrl); // connect ajax

    xhr.onload = function(){

      if (xhr.status < 200 || xhr.status >= 300) {
        callback('HTTP Error: ' + xhr.status); // show error to href
        return;
      }

      let json = JSON.parse(xhr.responseText);

      // API側(CodeIgniter側)のレスポンスJSONにlocationプロパティがない時
      if (!json || typeof json.location != 'string') {
        callback('Invalid JSON: ' + xhr.responseText); // show error to href
        return;
      }

      let fileUrl = json.location;
      let attrs = {
        "text"  :"", // リンク元テキスト
        "title" : file.name // title属性
      };

      // TinyMCEのリンクモーダルにhrefやtitleなどの値を返す
      callback(fileUrl, attrs);
    };

    xhr.send(postData);
  };
}

 

file 타입의 input 요소 생성

let allowedFileTypes = 'application/pdf';
let input = document.createElement('input');

input.setAttribute('type', 'file');
input.setAttribute('accept', allowedFileTypes);
input.click();

허용가능한 Type은 PDF이며, type속성은 file인 input 요소를 하나 생성을 합니다. 

그래고 해당 요소에 클릭 메소드를 실행 

 

그러면, TinyEdit에서 link -> 파일선택 버튼을 누르게되면, 위의 핸들러 함수가 실행되어 맨처음 file 타입의 input 요소를 만들고 클릭을 실행하기 때문에 파일 선택창이 나오게 됩니다. 

 

 

 input.onchange = function() {
    let file = this.files[0];

    // API側(CodeIgniter側)に送るデータを準備
    let postData = new FormData();
    
    // PDFデータを送る
    postData.append('body_pdf', file, file.name); // $_FILES['body_pdf'] => array 
}

사용자가, 파일을 선택을 하게되면, input에 변화가 생기게 되기 때문에 그것을 onchange 이벤트로 감지하여 익명 함수를 실행시킵니다. 

 

1. 선택되어진 file의 데이터를 변수에 저장

 

2. FormData 인스턴스를 생성

 

3. 생성된 FormData 인스턴스에 키와 값의 쌍으로 이루어진 데이터를 삽입 

 

 

FormData 인스턴스를 생성하는 이유 

file을 서버에 전송하고 저장하고 싶은 경우, HTML의 Form태그를 이용하려면, 아래와 같은 entype=" multipart/form-data라던가 지정해줘야만 하는 값들이 많습니다. 

 

<form action="#" method="post" enctype="multipart/form-data">
    <input type="file" name="file"/>
    <input type="submit" value="upload"/>
</form>

 

이러한 과정을 Javascript의 XMLHttpRequest를 이용하여, 파일을 전송하여 저장하고 싶은 경우에는 굉장히 귀찮은 작업이 될 수 있기 떄문에, FormData인스턴스를 생성하게 되면 위와 같은 과정을 생략할 수 있습니다. 

 

 postData.append('body_pdf', file, file.name); // $_FILES['body_pdf'] => array

인스턴스를 생성하고 append를 이용해 키와 값을 전송을 합니다. 

 

그럼 PHP에서는 글로벌 변수인 $_FILES로 해당 값을 받아올 수 있습니다. 마치 위에서 나온 form으로 전송하는 것과 동일하게 

 

 

XMLHttpRequest 통신

let filesUploadUrl = URL;

let xhr = new XMLHttpRequest();

xhr.open('POST', filesUploadUrl);

xhr.onload = function(){

    if (xhr.status < 200 || xhr.status >= 300) {
        callback('HTTP Error: ' + xhr.status); // show error to href
        return;
    }

    let json = JSON.parse(xhr.responseText);

    // API側(CodeIgniter側)のレスポンスJSONにlocationプロパティがない時
    if (!json || typeof json.location != 'string') {
        callback('Invalid JSON: ' + xhr.responseText); // show error to href
        return;
    }

    let fileUrl = json.location;
    let attrs = {
        "text"  :"", // リンク元テキスト
        "title" : file.name // title属性
    };

    // TinyMCEのリンクモーダルにhrefやtitleなどの値を返す
    callback(fileUrl, attrs);
};

xhr.send(postData);

XMLHttpRequest를 이용하여, File의 Form데이터를 PHP코드(Codeigniter 의Controller)로 전송시켜 파일을 저장합니다. 

 

 


파일 저장

 

Config/upload.php

파일 업로드를 제약하기 위한, 설정파일이 필요합니다. 

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');

$config['upload_path'] = upload_path;

$config['allowed_types'] = "pdf";

$config['max_size'] = '1000000';

$config['max_filename'] = '50';

저장될 파일의 위치

저장될 파일의 타입

파일의 최대 용량

파일이름의 최대 글자수 

 

 

위의 config설정 파일의 내용을 load해서 사용하기 위해, config/autoload.php에서 autoload에 upload를 추가로 넣어준다.

$autoload['config'] = array('upload');

 

Codeigniter Controller

public function upload_pdf()
{
    $fileDir = $this->uploadPdfDir();

    $inputName = "body_pdf";
    $file = $this->uploadFile($inputName);

    $filePath = $fileDir . $file['file_name'];

    // フロント(AJAX)に返すデータを準備
    $res = array(
        'location' => $filePath,
        'status' => 'success',
        'response' => 200
    );

    header("Content-Type: application/json; charset=utf-8");
    echo json_encode($res);
    return TRUE;
}

private function uploadPdfDir()
{
    return "URL";
}

protected function uploadFile($fieldName = "image")
{
    $config = $this->loadDefaultConfigUpload();

    $this->load->library('upload', $config);

    $ret = array();

    if($this->upload->do_upload($fieldName)){
        $info = $this->upload->data();
        $filePath = $info['file_path'];
        $fileName = $info['file_name'];

        $ret = array(
            'info' => $info,
            'file_name' => $fileName,
            'is_success' => TRUE,
            'response' => 200,
            'message' => 'successfully uploaded ' . $filePath . $fileName,
        );
    } else {
        $info = $this->upload->display_errors();

        $ret = array(
            'info' => $info,
            'file_name' => NULL,
            'is_success' => FALSE,
            'response' => 500,
            'message' => 'upload failed',
        );
    }

    return $ret;
}

private function loadDefaultConfigUpload()
{
    $props = array('upload_path', 'allowed_types', 'max_size', 'max_filename');
    $ret = array();

    foreach($props as $key){
        $ret[$key] = $this->config->item($key);
    }

    return $ret;
}

 

uploadPdfDir()

private function uploadPdfDir()
{
    return "URL";
}

PDF파일을 업로드할 디렉토리 위치를 반환합니다. 

 

loadDefaultConfigUpload()

private function loadDefaultConfigUpload()
{
    $props = array('upload_path', 'allowed_types', 'max_size', 'max_filename');
    $ret = array();

    foreach($props as $key){
        $ret[$key] = $this->config->item($key);
    }

    return $ret;
}

config/upload.php에서 저장한 upload 관련 config값들을 가져오기 위한 메소드이다. 

 

 

uploadFiles()

protected function uploadFile($fieldName = "image")
{
    $config = $this->loadDefaultConfigUpload();

    $this->load->library('upload', $config);

    $ret = array();

    if($this->upload->do_upload($fieldName)){
        $info = $this->upload->data();
        $filePath = $info['file_path'];
        $fileName = $info['file_name'];

        $ret = array(
            'info' => $info,
            'file_name' => $fileName,
            'is_success' => TRUE,
            'response' => 200,
            'message' => 'successfully uploaded ' . $filePath . $fileName,
        );
    } else {
        $info = $this->upload->display_errors();

        $ret = array(
            'info' => $info,
            'file_name' => NULL,
            'is_success' => FALSE,
            'response' => 500,
            'message' => 'upload failed',
        );
    }

    return $ret;
}

File을 upload하기 위해서 Codeigniter의 라이브러리 upload를 로드해줍니다. 

로드할 때에는, upload에 필요한 config 설정 값을 인수로 같이 넣어줍니다. 

같이 넣어주지 않으면,

$this->upload->initialize($config);

위와같이 따로 설정해줘야 합니다. 

 

파일 업로드

$this->upload->do_upload($fieldName)

실제로 파일을 upload하는 코드이며, 반환값은 bool값입니다. 

따라서, 성공 /실패의 결과값을 이용해 조건문 처리를 해주기 좋습니다. 

 

업로드 성공

$this->upload->data();

업로드를 성공했을 경우에, data() 메소드를 이용하면 업로드한 파일의 정보값을 받을 수 있습니다. 

 

업로드 실패 

$this->upload->display_errors();

업로드에 실패했을 때, display_errors() 메소드를 이용하면, 업로드에 실패한 이유를 얻을 수 있습니다. 

 

 

upload_pdf()

public function upload_pdf()
{
    $fileDir = $this->uploadPdfDir();

    $inputName = "body_pdf";
    $file = $this->uploadFile($inputName);

    $filePath = $fileDir . $file['file_name'];

    // フロント(AJAX)に返すデータを準備
    $res = array(
        'location' => $filePath,
        'status' => 'success',
        'response' => 200
    );

    header("Content-Type: application/json; charset=utf-8");
    echo json_encode($res);
    return TRUE;
}

파일을 업로드하고, 업로드된 파일의 위치, 상태정보등을 json타입으로 변환하여 반환하게 됩니다. 

 


콜백함수

파일 저장후, 문제가 없을 경우 콜백함수를 실행시켜 업로드한 파일의 정보를 기준으로 tinyEditor에 반영

 

let fileUrl = json.location;
let attrs = {
    "text"  :"", // リンク元テキスト
    "title" : file.name // title属性
};

callback(fileUrl, attrs);

json의 location에는 PHP코드로 저장된 PDF파일의 path값이고, 그 값을 fileUrl 변수에 저장합니다.

attars 변수에는 text와 title을 지정해주는데, 이 값들은 

 

TinyEditor의 link창에 나와있는 항목의 값을 가르킵니다. 

callback 함수의 위에 변수를 넣어줌으로써 link창에 해당 항목의 값이 적히게 됩니다.