Skip to main content
Deno 2 is finally here 🎉️
Learn more

This is a fork of https://github.com/hviana/Upload-middleware-for-Oak-Deno-framework with some import fixes


Original README

Upload middleware for Oak Deno framework

This middleware automatically organizes uploads to avoid file system problems and create dirs if not exists, perform validations and optimizes ram usage when uploading large files using Deno standard libraries!

Usage:

Ex:

.post("/upload", upload('uploads'), async (context: any, next: any) => { ...
.post("/upload", upload('uploads', { extensions: ['jpg', 'png'], maxSizeBytes: 20000000, maxFileSizeBytes: 10000000, saveFile: true, readFile: false, useCurrentDir: true, useDateTimeSubDir: true }), async (context: any, next: any) => { ...
.post("/upload", upload('uploads', { extensions: ['jpg', 'png'], useDateTimeSubDir: false }), async (context: any, next: any) => { ...

Uploads will be in context.uploadedFiles;

upload(

path,

extensions: optional ex: [‘jpg’, ‘png’], default allow all - [].

maxSizeBytes: optional, max total size in bytes for all files in form, default unlimited - Number.MAX_SAFE_INTEGER.

maxFileSizeBytes: optional, max size in bytes for each file in form, default unlimited - Number.MAX_SAFE_INTEGER.

saveFile: optional, if false the file will not be saved in the file system, and a temporary file generated by Deno std will returned in ‘tempfile’ field, default true.

readFile: optional, if true the file will be fully loaded on the ram and a Uint8Array will be returned in the ‘data’ field, default false.

useCurrentDir: optional, if true the path is relative to current Deno working directory, default true.

useDateTimeSubDir: optional, if true the file path will be prefixed with /year/month/day/hour/minute/second/uuid/filename e.g. /2020/4/4/20/0/28/1350065e-7053-429b-869b-08008a098b23/test.jpg, default true.

), next middlewares …

Request must contains a body with form type “multipart/form-data”, and inputs with type=”file”.

Ex:

.post("/pre_upload", preUploadValidate(["jpg", "png"], 20000000, 10000000), async (context: any, next: any) => { ...

preUploadValidate(

extensions: optional ex: [‘jpg’, ‘png’], default allow all - [],

maxSizeBytes: optional, max total size in bytes for all files in form, default unlimited - Number.MAX_SAFE_INTEGER,

maxFileSizeBytes: optional, max size in bytes for each file in form, default unlimited - Number.MAX_SAFE_INTEGER

), next middlewares …

This middleware does a pre-validation before sending the form, for optimizations. To use it, send a JSON containing the objects “file”. Use a different route than the upload route. Returns a validation message with all errors and status 422 (if there are errors).

Examples:

Below an example to work with AJAX, also accepting type=”file” multiple:

var files = document.querySelector('#yourFormId input[type=file]').files
var name = document.querySelector('#yourFormId input[type=file]').getAttribute('name');

var form = new FormData();
for(var i=0;i<files.length;i++){
    form.append(`${name}_${i}`, files[i]);	
}
var res = await fetch('/upload', { //Fetch API automatically puts the form in the format "multipart/form-data".
    method: 'POST',
    body: form,
}).then(response=>response.json())
console.log(res)

//VALIDATIONS --------------

var validationData = {}
for(var i=0;i<files.length;i++){
    var newObj = { //newObj is needed, JSON.stringify(files[i]) not work
       'name'             : files[i].name,
       'size'             : files[i].size
    }; 
    validationData[`${name}_${i}`] = newObj;
}
var validations = await fetch('/pre_upload', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify(validationData),
}).then(response=>response.json())
console.log(validations)

In Deno:

import { upload, preUploadValidate} from "https://deno.land/x/upload_middleware_for_oak_framework/mod.ts";

  .post("/upload", upload('uploads', { extensions: ['jpg', 'png'], maxSizeBytes: 20000000, maxFileSizeBytes: 10000000 }),
    async (context: any, next: any) => {
      context.response.body = context.uploadedFiles;
    },
  )
  .post("/pre_upload", preUploadValidate(["jpg", "png"], 20000000, 10000000),
    async (context: any, next: any) => {
      context.response.body = { msg: "Pass upload validations." };
    },
  )
  .get("/", async (context: any, next: any) => {
    context.response.body = `
            <form id="yourFormId" enctype="multipart/form-data" action="/upload" method="post">
              <input type="file" name="file1" multiple><br>
              <input type="submit" value="Submit">
            </form>
    `;
  })
  //This will return something like:
{
    "file1_0":{
        "filename":"test.jpg",
        "type":"image/jpeg",
        "size":16980,
        "id":"2020/4/4/20/0/28/1350065e-7053-429b-869b-08008a098b23",
        "url":"uploads/2020/4/4/20/0/28/1350065e-7053-429b-869b-08008a098b23/test.jpg",
        "uri":"C:\\Users\\Engenharia\\Documents\\base\\uploads\\2020\\4\\4\\20\\0\\28\\1350065e-7053-429b-869b-08008a098b23\\test.jpg"
    },
    "file1_1":{
        "filename":"download.png",
        "type":"image/png",
        "size":2623,
        "id":"2020/4/4/20/0/28/46698b10-d319-4bbb-af64-fc8b2b991b54",
        "url":"uploads/2020/4/4/20/0/28/46698b10-d319-4bbb-af64-fc8b2b991b54/download.png",
        "uri":"C:\\Users\\Engenharia\\Documents\\base\\uploads\\2020\\4\\4\\20\\0\\28\\46698b10-d319-4bbb-af64-fc8b2b991b54\\download.png"
    }
}

If you want, you can delete a file sent using:

await Deno.remove(context.uploadedFiles['file2']['uri']);

Or (if not save file):

await Deno.remove(context.uploadedFiles['file2']['tempfile']);

Remember that you need permissions:

deno run --allow-net --allow-read --allow-write ./server.ts