What I have to do when I need to encode the file to base64 but `ProgressEvent.target.result` is null or has `ArrayBuffer` type?

249 Views Asked by At

According to TypeScript types definitions, the type of ProgressEvent.target.result could be string, ArrayBuffer or null. What I need is, the function which encodes the file to base64 which is type string. But what I have to do when it is null or instance of ArrayBuffer?

async function encodeFileToBase64(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 => {

      const fileReadingResult: string | ArrayBuffer | null | undefined = filedHasBeenReadEvent.target?.result;

      if (isEitherUndefinedOrNull(fileReadingResult)) {
        reject(new Error("Failed to encode the file."));
        return;
      }


      resolve(
        fileReadingResult instanceof ArrayBuffer ?
            String.fromCharCode.apply(null, new Uint8Array(fileReadingResult)) :
            fileReadingResult
      );

    };

  });

}

The "Failed to encode the file." error has not enough details.

  • What means null?
  • Why has it occured?
  • What is required to do to have the string type next time?

Same sub-question about ArrayBuffer. Additionally, I have TypeScript error in

String.fromCharCode.apply(null, new Uint8Array(fileReadingResult))

S2345: Argument of type  Uint8Array  is not assignable to parameter of type  number[]  Type  Uint8Array  is missing the following properties from type  number[] :  pop, push, concat, shift , and  5  more.

2

There are 2 best solutions below

3
Dimava On
  1. You don't need to use event.target, as event.target is the "a reference to the object onto which the event was dispatched", so it's just the fileReader itself

  2. The type of fileReader.result depends on the operation you call over it. Depending on the operation, FileReader can be one of:

const fr: FileReader = new FileReader();
//        ^^^^^^^^^^ required for asserts to work
interface FileReader {
    readAsArrayBuffer(blob: Blob): asserts this is XOR<{ result: ArrayBuffer }, { error: DOMException }, null>;
    readAsBinaryString(blob: Blob): asserts this is XOR<{ result: string }, { error: DOMException }, null>;
    readAsDataURL(blob: Blob): asserts this is XOR<{ result: dataUrl }, { error: DOMException }, null>;
    readAsText(blob: Blob): asserts this is XOR<{ result: string }, { error: DOMException }, null>;
}

type XOR<A, B, Miss = never> = Prettify<
    | A & Omit<{ [K in keyof B]?: Miss }, keyof A>
    | B & Omit<{ [K in keyof A]?: Miss }, keyof B>
>
type Prettify<T> = T extends T ? { [K in keyof T]: T[K] } : never
  1. The value is null if the reading is not yet complete or was unsuccessful.. So, as you have already awaited the load event, if means that fileReader.error has the error value.

Personally I would recommend a wrapper of the whole FileReader like https://www.npmjs.com/package/@tanker/file-reader or a typed variant of MDN example:
https://developer.mozilla.org/en-US/docs/Web/API/Event/target#example

const reader = (file: File) =>
    new Promise<dataUrl>((resolve, reject) => {
        const fr = new FileReader();
        fr.onload = () => resolve(fr.result as dataUrl);
        fr.onerror = (err) => reject(fr.err);
        fr.readAsDataURL(file);
    });
type dataUrl = string & { _?: 'dataUrl' } // branded for clearness
0
Raymond Natio On
  1. 'null' is a variable that is defined, but is missing the value
  2. In practice, most of the 'null' values arise from human error during programming, and these two go together in most cases.
  3. readAsDataUrl will always return you a 'string' type

enter image description here

  1. The issue is String.fromCharCode expects an array of number[] as the argument. So you need to convert it from Uint8Array to normal array.

    String.fromCharCode.apply(null, [...new Uint8Array(fileReadingResult)])

Hope this helps you @TakeshiTokugawaYD!