Skip to content
Advertisement

How to upload a file using the fetch API in Javascript to a Laravel Controller?

I have a FILES variable that looks like this:

Output Example

I am trying to receive these files at my Laravel controller like so:

if(!empty($request->allFiles()))
    ...

var_dump($request->all()); # debug

I am posting to this endpoint using the fetch API like so:

let fd = new FormData();
fd.append('files[]', FILES)

fetch('{{ url()->to(route('gallery.upload')) }}', {
    method: 'POST',
    credentials: "same-origin",
    headers: {
      "X-CSRF-Token": '{{ csrf_token() }}'
    },
    body: fd,
})

In my response (network tab) I get:

array(1) { ["files"]=> array(1) { [0]=> string(15) "[object Object]" } }

I tried to JSON.Stringify(FILES) and got:

array(1) { ["files"]=> array(1) { [0]=> string(70) "{"blob:http://127.0.0.1:8000/502cbd0f-f2b7-4155-8475-d83e48bb9730":{}}" } }

Can anyone point me in the right direction to why the file is not posted? This is how the FILES is created.

const fileTempl = document.getElementById("file-template"),
      imageTempl = document.getElementById("image-template"),
      empty = document.getElementById("empty");

let FILES = {};

function addFile(target, file) {
    const isImage = file.type.match("image.*"),
    objectURL = URL.createObjectURL(file);

    const clone = isImage
        ? imageTempl.content.cloneNode(true)
        : fileTempl.content.cloneNode(true);

    // Creates nice output of the file
    clone.querySelector("h1").textContent = file.name;
    clone.querySelector("li").id = objectURL;
    clone.querySelector(".delete").dataset.target = objectURL;
    clone.querySelector(".size").textContent = file.size > 1024
        ? file.size > 1048576
            ? Math.round(file.size / 1048576) + "mb"
            : Math.round(file.size / 1024) + "kb"
        : file.size + "b";

    isImage &&
        Object.assign(clone.querySelector("img"), {
            src: objectURL,
            alt: file.name
        });

    empty.classList.add("hidden");
    target.prepend(clone);

    FILES[objectURL] = file;
}

Advertisement

Answer

You need to append files to your FormData object, not a plain object.

i.e. given formData.append("key", value), the value needs to be a file object (it can also be a string). It can’t be a plain object (like FILES in your example) as that will be converted to a string, which isn’t useful:

const object = {};
console.log(object.toString());

Manually extracting the files from a file input and appending them one-by-one is overly complex though. Typically you would just use a form instead of appending to the FormData object:

<form action="example" method="POST" enctype="multipart/form-data">
    <input type="file" name="files[]" multiple>
    <button>Submit</button>
</form>

<script>
    const form = document.querySelector('form');
    form.addEventListener('submit', event => {
        const data = new FormData(form);
        fetch('url', {
            method: 'POST',
            body: new FormData(form);
        });
    });
</script>
User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement