Block Gaming
Block Gaming

Reputation: 15

FileReader: reader.result is null when it clearly isnt

I've been trying to make my code work for hours and i'm getting so annoyed and tired of this and would like to ask for some help.

I'm trying to make a easy API for the file input type (don't start saying my code is bad, i just want something that works and would like to ask for your help. Thank you.

I know that onload is asynchronous and I've tried every possible thing i can think of and this is irritating me. Ill be checking back here later on to see if anyone could help.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File Input - Testing</title>
    <script src="js/app_2.js" defer></script>
</head>
<body>
    <input type="file" id="file_input" />
    <img id="image_preview" />
</body>
</html>
let file_input;
document.addEventListener("DOMContentLoaded", function() {
    file_input = document.querySelector("#file_input");
    file_input.addEventListener("change", () => on_file_update());
});

function FileInputManager(files) {
    if (!files || typeof files !== "object")
        return console.error("FileInputManager: Missing file_input_element.files, please provide it so I can run!");

    let reader = new FileReader();
    let file = files[0];
    let types = file.type.split("/");
    let full_type = types[0] === "" ? "empty" : types.join("/");

    if (!file)
        return console.error("FileInputManager: No file selected, maybe something went wrong?");

    // Determine How To Read The File By Testing The Mime-Type
    this.determine_read_type = function() {
        switch (types[0]) {
            case "image":
                return reader.readAsDataURL(file);
            case "text":
                return reader.readAsText(file);
            case "application":
                return reader.readAsText(file);
            default:
                console.warn(`FileInputManager: File reading has been aborted, file type [type: ${full_type}] unsupported.`);
                break;
        }
    }

    this.determine_read_type();

    let result = "unknown";
    reader.onload = function() {
        result = reader.result;
    };

    // switch(types[1]) {
    //     case "octet-stream":
    //         return console.error(`FileInputManager: Unable to run, file type [type: ${full_type}] not allowed.`);


    this.getResult = function() {
        return result;
    }

    this.getMimeType = function() {
        if (types.length <= 0)
            return console.error("FileInputManager: Mime-Type is missing, maybe something went wrong?");
        return String(types.join("/"));
    }

    return;
}

function on_file_update() {
    let reader_file = new FileInputManager(file_input.files);
    console.log(reader_file.getResult());
    if (reader_file.getMimeType().startsWith("image"))
        document.querySelector("#image_preview").src = reader_file.getResult();

    if (reader_file.getMimeType().startsWith("text"))
        console.log(reader_file.getResult());
}

Upvotes: 1

Views: 3033

Answers (2)

Shubham Khatri
Shubham Khatri

Reputation: 281764

As you correctly state that onLoad is asynchronous, but you still try to access the result immediately after calling new FileInputManager(file_input.files); in below line

let reader_file = new FileInputManager(file_input.files);
console.log(reader_file.getResult());

You instead need to return a promise from FileInputManager's getResult and wait on it

function FileInputManager(files) {
    let callbackRes = () => {};
    const promise = new Promise((res, rej ) => {
        this.callbackRes = res;
    });

    if (!files || typeof files !== "object")
        return console.error("FileInputManager: Missing file_input_element.files, please provide it so I can run!");

    let reader = new FileReader();
    let file = files[0];
    let types = file.type.split("/");
    let full_type = types[0] === "" ? "empty" : types.join("/");

    if (!file)
        return console.error("FileInputManager: No file selected, maybe something went wrong?");

    // Determine How To Read The File By Testing The Mime-Type
    this.determine_read_type = function() {
        switch (types[0]) {
            case "image":
                return reader.readAsDataURL(file);
            case "text":
                return reader.readAsText(file);
            case "application":
                return reader.readAsText(file);
            default:
                console.warn(`FileInputManager: File reading has been aborted, file type [type: ${full_type}] unsupported.`);
                break;
        }
    }

    this.determine_read_type();

    let result = "unknown";
    reader.onload = function() {
        callbackRes(reader.result);
    };

    // switch(types[1]) {
    //     case "octet-stream":
    //         return console.error(`FileInputManager: Unable to run, file type [type: ${full_type}] not allowed.`);


    this.getResult = function() {
        return promise;
    }

    this.getMimeType = function() {
        if (types.length <= 0)
            return console.error("FileInputManager: Mime-Type is missing, maybe something went wrong?");
        return String(types.join("/"));
    }

    return;
}

async function on_file_update() {
    let reader_file = new FileInputManager(file_input.files);
    const res = await reader_file.getResult();
    console.log(res);
    if (reader_file.getMimeType().startsWith("image"))
        document.querySelector("#image_preview").src = reader_file.getResult();

    if (reader_file.getMimeType().startsWith("text"))
        console.log(res);
}

Upvotes: 2

T.J. Crowder
T.J. Crowder

Reputation: 1074495

You're calling getResult before the result is available. As you said, the reading process is asynchronous, but your code isn't waiting for the read to be complete. You start the process in FileInputManager, but then on_file_update immediately calls getResult, which is still "unkonwn" because nothing has assigned to it since you assigned "unknown".

You have to wait for that onload callback before you can use the result. FileInputManager will need to provide some way of hooking into the callback it gets — letting you provide your own callback, returning a promise, that sort of thing.

Upvotes: 2

Related Questions