[Express][TypeScript] Downloading files
Tags: typescript, express, node
Published: 2021-06-25 22:50:05

## Intro
This time, I will try downloading files.

* [[Express][TypeScript] Uploading file 1](https://dev.to/masanori_msl/express-typescript-uploading-file-1-1538)

## Download files by Stream
I can download files by fs like below.

#### fileDonwloader.ts
```ts
import fs from 'fs';

export function loadFile(): fs.ReadStream {
    return fs.createReadStream('tmp/region.png');
}
```

#### index.ts
```ts
import express from 'express';
...
import * as downloader from './files/fileDownloader';

const port = 3000;
const app = express();
...
app.get('/files/download', (req, res) => {
    const stream = downloader.loadFile();
    // download file as "hello.png"
    res.writeHead(200, {
        "Content-Type": "image/png",
        "Content-Disposition": "attachment; filename=hello.png",
    });
    stream.on('open', () => {
        console.log('Opened');
        stream.pipe(res);
    });
    stream.on('close', () => {
        console.log('Closed');
    });
});
app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`)
});
```

It's very simple.
After opening the stream, downloading the file is started.
After finishing the downloading, the stream is closed.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wwa2p2ucz9cke1vn7zrm.jpg)

* [How to use fs.createReadStream? | Node.js](https://nodejs.org/en/knowledge/advanced/streams/how-to-use-fs-create-read-stream/)
* [Content-Disposition - HTTP | MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition)

## Play videos
How about playing videos?

First, I try setting the url as "src" of a video element.

#### [Client] index.html
```html
<!DOCTYPE html>
<html lang='en'>
    <head>
        <title>Hello</title>
        <meta charset="utf8">
    </head>
    <body>
...
        <video muted controls autoplay src="/files/video"></video>
        <script src="./js/main.page.js"></script>
        <script>Page.init()</script>
    </body>
</html>
```

#### [Server] index.ts
```ts
app.get('/files/video', (req, res) => {
    const stream = downloader.loadVideo();
    stream.on('open', () => {
        console.log('Opened');
        
        stream.pipe(res);
    });
    stream.on('close', () => {
        console.log('Closed');
    });
});
```

#### [Server] fileDownloader.ts
```ts
import fs from 'fs';
...
export function loadVideo(): fs.ReadStream {
    return fs.createReadStream('tmp/sample.mp4');
}
```
I can play the video.
But I have a problem.

After loading the page, before starting the video, I must wait for downloading data first.
After donwloading, the video start playing.
But the data isn't the first one.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oruesuys5uyhu4fk67b9.jpg)

I think it's for getting the video information to prepare playing.
Can I avoid the first downloading or finish more quickly?

### Download videos by TypeScript
How about donwload the video and set it into the video element by TypeScript?


#### [Client] index.html
```html
...
        <video id="video_sample"></video>
        <script src="./js/main.page.js"></script>
        <script>Page.init()</script>
    </body>
</html>
```

#### [Client] main.page.ts
```ts
...
export function init() {
    const targetVideo = document.getElementById('video_sample') as HTMLVideoElement;
    targetVideo.muted = true;
    targetVideo.controls = true;
    targetVideo.autoplay = true;

    fetch('files/video', {
        method: 'GET',
        mode: 'cors'
    })
    .then(res => res.blob())
    .then(data => {
        targetVideo.src = URL.createObjectURL(data);
    })
    .catch(err => console.error(err));
}
```

Most of the result was same as first example.
Because it also waits for downloading all of the video data first.

The difference is caching  the video data, so though I replay the video after finishing the first playing, it won't download the data again.

Can I set Stream API as the source of the video element?
I couldn't find how to do that :(

If I can generate "MediaStream" or "MediaSource" from ReadableStream, I can set as "srcObject" of the video element...

Shall I use WebSocket or WebRTC?

## Resources
* [4.8.9 The video element - HTML Standard](https://html.spec.whatwg.org/multipage/media.html#the-video-element)
* [video: The Video Embed element - HTML: HyperText Markup Language | MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video)
* [Live streaming web audio and video - Developer guides | MDN](https://developer.mozilla.org/en-US/docs/Web/Guide/Audio_and_video_delivery/Live_streaming_web_audio_and_video)