Uploading files with Deno
Tags: deno, typescript, tutorial, javascript
Published: 2021-04-11 15:46:24

Here I will show a quick tutorial of how to upload files using Deno. Files will simply be stored to disk along with the ability to view them in a browser.

First of all, install [Deno](https://deno.land/).

Now we will add a file to serve our app. Create a project folder to contain your app, then add a subfolder called **server**. We will also create a client to test it, having a server and client folder will help organize our project.

Add a server.ts file to your server folder. And add the code:
```javascript
import { Application, send } from "https://deno.land/x/oak/mod.ts";
import { FormFile, multiParser } from 'https://deno.land/x/multiparser@v2.1.0/mod.ts'
import { oakCors } from "https://deno.land/x/cors/mod.ts";

const app = new Application();

app.use(
  oakCors({
    origin: true
  }),
);

app.use(async (ctx) => {
  const path = ctx.request.url.pathname;
  if (path === '/upload') {
    const form = await multiParser(ctx.request.serverRequest)
    if (form) {
      const image: FormFile = form.files.image as FormFile
      try {
        await Deno.writeFile(`images/${image.filename}`, image.content);
      } catch (e) {
        console.error(e)
      }
    }
    ctx.response.body = '{"status": "ok"}';
  }

  if (ctx.request.method === 'GET' && path.startsWith('/images')) {
    await send(ctx, ctx.request.url.pathname, {
      root: `${Deno.cwd()}`
   });
  }
});

app.listen({ port: 8082 });
```

What is happening here?  
* At the top we import our dependencies
* Then we create an app, then app.use to register middleware.  
* oakCors will allow any client to call the server (ok for a demo)
* In the second app.use block we define routes: **/upload** and **/images**. From the client we will post to **/upload** and we can use **/images** to view the images.
* /upload gets a file from a posted form and writes it to a folder

Now let's run the app. We need to set permissions to allow-net for http calls, and allow-write/allow-read for writing and reading the images. Then we tell deno to run our server.ts file.

```shell
deno run \
--allow-net \
--allow-write=./images \
--allow-read=./,./images \
./server/server.ts
```

Now let's add the client. Add a **client** folder and create an index.html file with the following:

```html
<html>
  <head>
    <script src="./index.js"></script>
  </head>
  <body>
    <input type="file" name="file" id="imgfile" onchange="loadImage()">
    <input type='button' id='btnLoad' value='Upload' onclick="upload()" />
    <canvas id="canvas"></canvas>
  </body>
</html>
```

Now add an index.js file with:

```javascript
async function post(canvas, name) {
  const ts = new Date().getTime();
  canvas.toBlob(async function(blob) {
    const formData = new FormData();
    formData.append('image', blob, name);
    const res = await fetch('http://localhost:8082/upload', {
      mode: 'no-cors',
      method: 'POST',
      body: formData
    });
  });
}

function loadImage() {
  let img;

  const input = document.getElementById('imgfile');
  if (!input.files[0]) {
      write("Please select a file before clicking 'Load'");
      return;
  }

  const file = input.files[0];
  const fr = new FileReader();
  fr.onload = createImage;
  fr.readAsDataURL(file);

  function createImage() {
      img = new Image();
      img.onload = imageLoaded;
      img.src = fr.result;
  }

  function imageLoaded() {
      const canvas = document.getElementById("canvas")
      canvas.width = img.width;
      canvas.height = img.height;
      const ctx = canvas.getContext("2d");
      ctx.drawImage(img,0,0);
  }

}

async function upload() {
  const canvas = document.getElementById("canvas")
  const input = document.getElementById('imgfile');
  await post(canvas, input.files[0].name);
  write('File uploaded')
}

function write(msg) {
  const p = document.createElement('p');
  p.innerHTML = msg;
  document.body.appendChild(p);
}
```

Now open the index.html file, select your image, and upload it! We could serve this page with Deno, but we don't really need to for this demo.

To view your uploaded image, you can go to: localhost:8082/images/[yourimagename]

Here is the full [GitHub Project](https://github.com/scottlepp/deno-file-upload-example)

As you can see, [Deno](https://deno.land/) is super easy, fun and fast! Happy coding!