ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [삭제대기] 텍스트 에디터(CKEDITOR5 + VUE + LARAVEL) 이미지 업로드
    Laravel 2021. 12. 27. 16:09
    반응형
    SMALL

    # What?

    - 텍스트에디터로 이미지 업로드하는법

     

    # How?

    1. 기본 세팅

    1) ckeditor5 온라인 빌더로 원하는 기능 추가해서 생성하기

    https://ckeditor.com/ckeditor-5/online-builder/

     

    CKEditor 5 Online Builder | Create your own editor in 5 steps

    Create your own CKEditor 5 build with customized plugins, toolbar and language in 5 simple steps.

    ckeditor.com

     

    2) build폴더 public/js/ckeditor 폴더에 옮기기

     

    4) ckeditor js 연결 및 csrf 토큰 메타태그 추가

     

    @ app.blade.php

    <!DOCTYPE html>
    <html>
    <head>
        ...
        <script src="{{ asset('/js/ckeditor/build/ckeditor.js') }}" defer></script>
        <meta name="csrf-token" content="{{ csrf_token() }}">
       
    <script>
    <body>
    @inertia
    </body>
    </html>

     

    2. 커스텀 어댑터 세팅

    @ Utils/MyUploadAdapter.js

    class MyUploadAdapter {
        constructor(loader) {
            this.loader = loader;
        }
    
        upload() {
            return this.loader.file
                .then(file => new Promise((resolve, reject) => {
                    this._initRequest();
                    this._initListeners(resolve, reject, file);
                    this._sendRequest(file);
                }));
        }
    
        abort() {
            if (this.xhr) {
                this.xhr.abort();
            }
        }
    
        _sendRequest(file) {
            const data = new FormData();
    
            data.append('upload', file);
    
            this.xhr.send(data);
        }
    
        _initListeners(resolve, reject, file) {
            const xhr = this.xhr;
            const loader = this.loader;
            const genericErrorText = `Couldn't upload file: ${file.name}.`;
    
            xhr.addEventListener('error', () => reject(genericErrorText));
            xhr.addEventListener('abort', () => reject());
            xhr.addEventListener('load', () => {
                const response = xhr.response;
    
                if (!response || response.error) {
                    return reject(response && response.error ? response.error.message : genericErrorText);
                }
    
                resolve({
                    default: response.url
                });
            });
    
            if (xhr.upload) {
                xhr.upload.addEventListener('progress', evt => {
                    if (evt.lengthComputable) {
                        loader.uploadTotal = evt.total;
                        loader.uploaded = evt.loaded;
                    }
                });
            }
        }
    
        _initRequest() {
            const xhr = this.xhr = new XMLHttpRequest();
    
            // 핵심
            xhr.open('POST', '/ckeditor/upload', true);
            xhr.setRequestHeader('x-csrf-token', document.querySelector('meta[name="csrf-token"]').getAttribute('content'));
            xhr.responseType = 'json';
        }
    
    
    }
    
    export default MyUploadAdapter

    * 그대로 복붙하고 주석 "// 핵심" 부분만 자기가 이미지 업로드 시 서버요청할 url로 변경하면됨

     

    @ CustomCkeditor.js

    import MyUploadAdapter from "./MyUploadAdapter";
    
    function SimpleUploadAdapterPlugin(editor) {
        editor.plugins.get("FileRepository").createUploadAdapter = (loader) => {
            return new MyUploadAdapter(loader);
        };
    }
    
    class CustomCkeditor {
        constructor(target = "#editor", onCreate = () => {}) {
            this.target = target;
    
            this.onCreate = onCreate;
        }
    
        create(){
            return ClassicEditor.create( document.querySelector( '#editor' ), {
                licenseKey: '',
                extraPlugins: [SimpleUploadAdapterPlugin],
            } ).then( editor => {
                window.editor = editor;
    
                // 세팅 완료 후 할일이 있다면
                this.onCreate();
    
            }).catch( error => {
                console.error( error );
            });
        }
    }
    
    export default CustomCkeditor

     

    @ Edit.vue

    <template>
        <form class="form type02" @submit.prevent="update">
            <div class="m-input-wrap type01">
                <div class="m-input-text type01">
                    <input type="text" placeholder="주소" v-model="form.address">
                    <p class="m-input-error" v-if="form.errors.address">{{ form.errors.address }}</p>
                </div>
            </div>
    
            <textarea v-model="form.description" id="editor"></textarea>
    
            <button class="m-btn type01 bg-primary">저장하기</button>
        </form>
    </template>
    <script>
    import CustomCkeditor from "../../Utils/CustomCkeditor";
    
    export default {
        data() {
            return {
                form: this.$inertia.form({
                    address: this.$page.props.user.data.address,
                    description: this.$page.props.user.data.description,
                }),
            }
        },
    
        methods: {
            update() {
                this.form.description = window.editor.getData();
    
                this.form.post("/users/update", {
                    forceFormData: true,
                    preserveScroll: true,
                    onSuccess: (response) => {
                        if (response.props.flash.error)
                            alert(response.props.flash.error);
    
                        if (response.props.flash.success)
                            alert(response.props.flash.success);
                    }
                });
            },
        },
    
        mounted() {
            new CustomCkeditor("#editor").create();
        }
    }
    </script>

    * 주석 "//핵심" 부분 보면 form 요청을 보낼 때 editor로부터 입력된 데이터를 받아와서 저장시켜줌.

     

    @ style.css

    .ck-editor__editable {
        min-height: 600px;
    }
    .ck-editor h1 {font-size:30px; font-weight:600;}
    .ck-editor h2 {font-size:24px; font-weight:600;}
    .ck-editor h3 {font-size:20px; font-weight:600;}
    .ck-editor ol li {list-style-type: decimal;}
    .ck-editor ol li {list-style-type: decimal;}

    3. 백엔드 세팅

    1) 라우팅 세팅

    @ web.php

    Route::post("/ckeditor/upload", [\App\Http\Controllers\CkeEditorController::class, "upload"])->name('ckeditor.upload');

     

    2) 모델 세팅

    @ Ckeditor.php

    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Database\Eloquent\Model;
    use Spatie\MediaLibrary\HasMedia;
    use Spatie\MediaLibrary\InteractsWithMedia;
    
    class Ckeditor extends Model implements HasMedia
    {
        use HasFactory, InteractsWithMedia;
    
        protected $appends = ["file"];
    
        public function registerMediaCollections():void
        {
            $this->addMediaCollection('file');
        }
    }

    * migration은 별다른 컬럼 추가할 필요 없음

     

    3) 컨트롤러 세팅

    @ CkeditorController.php

    <?php
    
    namespace App\Http\Controllers;
    
    use App\Models\Ckeditor;
    use Illuminate\Http\Request;
    
    class CkeEditorController extends Controller
    {
        public function upload(Request $request)
        {
            $ckeEditor = Ckeditor::first();
    
            if(!$ckeEditor)
                $ckeEditor = Ckeditor::create();
    
            if($request->hasFile("upload")){
                $media = $ckeEditor->addMedia($request->upload)->toMediaCollection("img", "s3");
    
                return response()->json([
                    "url" => $media->getFullUrl()
                ]);
            }
        }
    }

     

    LIST

    댓글

Designed by Tistory.