Search code examples
javascripthtmlgraphqlckeditorckeditor5

How to receive the URL of uploaded image and pass it to "src" attribute in CK Editor?


With Base64 image upload adapter, CKEditor encoding image obviously, to Base64 format, and inserts the result as <img src="data:image/png;base64, code... >. The code could be very long; I want uploaded image URL instead.

In my application, I need below functionality:

  1. Convert image to Base64. I know how to do it basically:
async function encodeSingleFileTo64base(targetFile: File): Promise<string> {

  const fileReader: FileReader = new FileReader();

  fileReader.readAsDataURL(targetFile);
  
  return new Promise<string>((resolve: (encodedFile: string) => void, reject: (error: Error) => void): void => {
    fileReader.onload = (filedHasBeenReadEvent: ProgressEvent<FileReader>): void => {
      if (filedHasBeenReadEvent.target === null || filedHasBeenReadEvent.target.result === null) {
        reject(new Error("Failed to encode the image file."));
        return;
      }
      resolve(String(filedHasBeenReadEvent.target.result));
    };
  });
}
  1. Submit Base64 code by GraphQL. I can do it basically:
import AWSAmplifyAPI, { graphqlOperation, GraphQLResult } from "@aws-amplify/api";

async function uploadPhotoAndGetURL(photoBase64: string): Promise<string> {
  return (await AWSAmplifyAPI.graphql(graphqlOperation(
    `mutation($photoBase64: String!) { uploadPhoto(photoBase64: $photoBase64) }`, 
    { photoBase64 }
  ))).uploadPhoto;  
}
  1. Make CK Editor to add received from response URL to src="" (it is the subject of current question).

Here is the solution template from documentation:

class MyUploadAdapter {

  constructor( loader ) {
      this.loader = loader;
  }

  
  upload() {
      return loader.file
          .then( file => server.upload( file ) );
  }

 
  abort() {
      server.abortUpload();
  }
}

First problem: how to chain two async functions in upload() method?

Both encoding and data submitting are async operations. I has been confused how to do it compatibly with loader.file.then().

Second problem: how to pass recieved URL to CK editor?

I can not undestand from suggested solution template how we can receive uploaded image URL and pass it to src attribute.


Solution

  • First problem: how to chain two async functions in upload() method?

    From here: https://thoughtbot.com/blog/chaining-events-like-a-boss

    Example:

    const timeUserRequest = async () => {
      const before = await getCurrentTimeAsync()
      await getUserDataViaHttp()
      const after = await getCurrentTimeAsync()
    
      return (after - before)
    };
    

    Second problem: how to pass recieved URL to CK editor?

    How are you making your request? Did you see this example ?

    class MyUploadAdapter {
        // ...
    
        // Initializes XMLHttpRequest listeners.
        _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;
    
                // This example assumes the XHR server's "response" object will come with
                // an "error" which has its own "message" that can be passed to reject()
                // in the upload promise.
                //
                // Your integration may handle upload errors in a different way so make sure
                // it is done properly. The reject() function must be called when the upload fails.
                if ( !response || response.error ) {
                    return reject( response && response.error ? response.error.message : genericErrorText );
                }
    
                // If the upload is successful, resolve the upload promise with an object containing
                // at least the "default" URL, pointing to the image on the server.
                // This URL will be used to display the image in the content. Learn more in the
                // UploadAdapter#upload documentation.
                resolve( {
                    default: response.url
                } );
            } );
    
            // Upload progress when it is supported. The file loader has the #uploadTotal and #uploaded
            // properties which are used e.g. to display the upload progress bar in the editor
            // user interface.
            if ( xhr.upload ) {
                xhr.upload.addEventListener( 'progress', evt => {
                    if ( evt.lengthComputable ) {
                        loader.uploadTotal = evt.total;
                        loader.uploaded = evt.loaded;
                    }
                } );
            }
        }
    }