Uczymy się tworzyć linki do downloadów, w JavaScript oraz w PHP. Do dzieła.

Ok, pracujemy tam, gdzie mamy evil.php i evli.txt. Tworzymy plik html i dodajemy taki link:

<a href="./evil.txt" download>Download evil.txt</a>

To nam ściągnie plik pod oryginalną nazwą. Możemy zrobić inną nazwę:

<a href="./evil.txt" download="iplist.txt">Download evil.txt</a>

Ok, a teraz coś ciekawego – download mamy i to z nazwą, ale href pusty:

<a download="hello.txt" href='#' id="link">Download</a>

I do czego to będzie? Cóż, możemy się już domyślić – sami, dynamicznie utworzymy plik a następnie link do ściągnięcia:

let blob = new Blob(["Hello, world!"], {type: 'text/plain'});
let link = document.querySelector("#link")
link.href = URL.createObjectURL(blob);

Klasa URL ma też statyczną metodę revokeObjectURL, to działa w zależności od przeglądarki, pamiętajmy że może nam popsuć link, więc nie wywołujmy tego, zanim nie chcemy aby link był zdatny do użycia.

Ok, teraz coś ciekawszego: tworzymy bloba HTMLowego:

<a download="hello.html" href='#' id="link2">Download</a>

I w JS:

let html = `
    <html>
      <head>
        <meta charset="UTF-8">
        <title>Title</title>
      </head>
      <body>
        <h1>Hello World</h1>
        <hr />
        <p>Lorem Ipsum</p>
      </body>
    </html>
  `;
let blob2 = new Blob([html], {type: 'text/html'});
let link2 = document.querySelector("#link2")

link2.href = URL.createObjectURL(blob2);

Widać już, do czego może się przydać znajomość tagged template literals. Ok, a jak ich nie lubimy – może takie coś zadziała?

let fragment = document.createDocumentFragment();
fragment.appendChild(
    document.createElement("h1")
    .appendChild(document.createTextNode("Hello World"))
);
fragment.appendChild(
    document.createElement("p")
    .appendChild(document.createTextNode("Lorem Ipsum"))
);
let html = `
    <html>
      <head>
        <meta charset="UTF-8">
        <title>Title</title>
      </head>
      <body>
        ${fragment.innerHTML}
      </body>
    </html>
  `;
let blob2 = new Blob([html], {type: 'text/html'});
let link2 = document.querySelector("#link2")

link2.href = URL.createObjectURL(blob2);

Nie, documentfragment ma undefined innerHTML. Ok, to może toString?

let html = `
    <html>
      <head>
        <meta charset="UTF-8">
        <title>Title</title>
      </head>
      <body>
        ${fragment.toString()}
      </body>
    </html>
  `;

Nie, dostaliśmy plik HTML o treści poniższej:

[object DocumentFragment]

Cóż, dziedziczy (documentfragment) z Node, ale nie Element, nie ma innerHTML. Ma textContent, ale tam znowu jest tylko tekst, bez tagów.

Na StackOverflow znalazłem taki kodzik, mówią, że trzeba pokombinować z template, który jest elementem:

export default function StringToFragment(string) {
    var renderer = document.createElement('template');
    renderer.innerHTML = string;
    return renderer.content;
}

Tylko właśnie, kod dobry, ale odpowiedź zła, bo content zwraca documentfragment. W naszym kontekście nie tego chcemy, my chcemy text jako html.

Cóż, może gdzieś się doczytam jak to zrobić, ale na chwilę obecną jedyna rozsądna opcja to jest tagged template literals.

Oczywiście możemy tworzyć nasz html „z palca”, pisząc cały string albo dodając += kawałki tekstu, ale to trochę straszne…

Ach, jeszcze miałem w PHP pokazać, jak zrobić link. Ok:

 <?php 
   
    header('Content-Type: text/plain');
    header('Content-Disposition: attachment; filename="evil.txt"');
    readfile('./evil.txt');
    ?>

Teraz jak tu wejdziemy to nam się ściągnie plik.