Upload Files Using Pre-signed URLs Slack

Using pre-signed URLs, a client can upload files directly to an S3-compatible cloud storage server (S3) without exposing the S3 credentials to the user.

This guide describes how to use the presignedPutObject API from the Minio JavaScript Library to generate a pre-signed URL. This is demonstrated through a JavaScript example in which an Express Node.js server exposes an endpoint to generate a pre-signed URL and a client-side web application uploads a file to Minio Server using that URL.

  1. Create the Server
  2. Create the Client-side Web Application

1. Create the Server

The server consists of an Express Node.js server that exposes an endpoint called /presignedUrl. This endpoint uses a Minio.Client object to generate a short-lived, pre-signed URL that can be used to upload a file to Mino Server.

// In order to use the Minio JavaScript API to generate the pre-signed URL, begin by instantiating
// a `Minio.Client` object and pass in the values for your server.
// The example below uses values for play.minio.io:9000

const Minio = require('minio')

var client = new Minio.Client({
    endPoint: 'play.minio.io',
    port: 9000,
    secure: true,
    accessKey: 'Q3AM3UQ867SPQQA43P2F',
    secretKey: 'zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG'
})

// Instantiate an `express` server and expose an endpoint called `/presignedUrl` as a `GET` request that
// accepts a filename through a query parameter called `name`. For the implementation of this endpoint,
// invoke [`presignedPutObject`](https://docs.minio.io/docs/javascript-client-api-reference#presignedPutObject) 
// on the `Minio.Client` instance to generate a pre-signed URL, and return that URL in the response:

// express is a small HTTP server wrapper, but this works with any HTTP server
const server = require('express')()

server.get('/presignedUrl', (req, res) => {
    client.presignedPutObject('uploads', req.query.name, (err, url) => {
        if (err) throw err
        res.end(url)
    })
})

server.get('/', (req, res) => {
    res.sendFile(__dirname + '/index.html');
})

server.listen(8080)

2. Create the Client-side Web Application

The client-side web application's user interface contains a selector field that allows the user to select files for upload, as well as a button that invokes an onclick handler called upload:

<input type="file" id="selector" multiple>
<button onclick="upload()">Upload</button>

<div id="status">No uploads</div>

<script src="//code.jquery.com/jquery-3.1.0.min.js"></script>
<script type="text/javascript">

// `upload` iterates through all files selected and invokes a helper function called `retrieveNewURL`.
function upload() {
   [$('#selector')[0].files].forEach(fileObj => {
     var file = fileObj[0]
     // Retrieve a URL from our server.
     retrieveNewURL(file, url => {
       // Upload the file to the server.
       uploadFile(file, url)
     })
   })
}

//`retrieveNewURL` accepts the name of the current file and invokes the `/presignedUrl` endpoint to
// generate a pre-signed URL for use in uploading that file: 
function retrieveNewURL(file, cb) {
   $.get(`/presignedUrl?name=${file.name}`, (url) => {
     cb(url)
   })
}

// ``uploadFile` accepts the current filename and the pre-signed URL. It then invokes `XMLHttpRequest()`
// to upload this file to S3 at `play.minio.io:9000` using the URL:
function uploadFile(file, url) {
     var xhr = new XMLHttpRequest ()
     xhr.open('PUT', url, true)
     xhr.send(file)
     xhr.onload = () => {
       if (xhr.status == 200) {
         $('#status').text(`Uploaded ${file.name}.`)
       }
     }
}
</script>

Note: This web application uses jQuery.