UploadHandler uploads files

From v1.59 UploadHandler is deprecated. Use DriveHandler.

UploadHandler lets you upload files and manage them. Here is a sample configuration:

url:
  uploadhandler:
    pattern: /$YAMLURL/upload
    handler: UploadHandler
    kwargs:
      path: $GRAMEXDATA/apps/guide/upload/ # ... save files here

By default, .meta.db keystore is created in kwargs:path directory to store uploaded files’ info. You can configure store as follows:

url:
  uploadhandler:
    pattern: /$YAMLURL/upload
    handler: UploadHandler
    kwargs:
      path: $GRAMEXDATA/apps/guide/upload/
      store:
        type: sqlite
        path: $GRAMEXDATA/apps/guide/upload/.fileinfo.db
        flush: 5

The type: can define any valid store type as seen in session data.

Note: Till 1.37 type: hdf5 was the default. From 1.38 onwards type:sqlite is defaulted.

Any file posted with a name of file is uploaded. Here is a sample HTML form:

<form action="upload" method="POST" enctype="multipart/form-data">
  <input name="file" type="file" />
  <button type="submit">Submit</button>
  <input type="hidden" name="_xsrf" value="{{ handler.xsrf_token }}" />
</form>

(See the XSRF documentation to understand xsrf_token.)

Try the uploader example

After the file is uploaded, users can be redirected via the redirect: config documented the redirection configuration.

AJAX uploads

DropZone provides drag-and-drop AJAX uploads with progress bars. For example:

<link rel="stylesheet" href="ui/dropzone/dist/min/dropzone.min.css" />
<form action="upload" class="dropzone"></form>
<script src="ui/dropzone/dist/min/dropzone.min.js"></script>

creates this box:

Saving uploads

By default, the file is saved in the path: specified by gramex.yaml, with the filename passed by the browser.

You can change the path where the file is saved using <input name="save">. See the example below:

<form action="upload" method="POST" enctype="multipart/form-data">
  <input name="file" type="file" />
  <button type="submit">Submit</button>
  <input type="hidden" name="save" value="folder/data.csv" />
  <input type="hidden" name="_xsrf" value="{{ handler.xsrf_token }}" />
</form>

This saves the file under folder/data.csv, which is under the path: section specified by gramex.yaml. folder/ is created if required.

If multiple file uploads are present, multiple save fields can be used to specify the filenames in order.

If the save value refers to a path outside of the path: specified, the handler returns a HTTP 403.

To disable users from overwriting the filename, you can set keys.save: [] in gramex.yaml. See Upload arguments

Overwriting uploads

If the target location already exists, there are 4 ways of handling it. This is specified by the overwrite: key in gramex.yaml.

    handler: UploadHandler
    kwargs:
      path: ...
      if_exists: error        # Raises a HTTP 403 with a reason saying "file exists"
      if_exists: backup       # Move the original to filename.YYYYMMDD-HHMMSS.ext
      if_exists: overwrite    # Overwrite the original without backup
      if_exists: unique       # Save to a new file: filename.1, filename.2, etc

Upload listing

You can retrieve the list of files uploaded via AJAX if you include a methods: GET in the kwargs: section as follows:

url:
  uploadhandler:
    pattern: /$YAMLURL/upload
    handler: UploadHandler
    kwargs:
      path: $GRAMEXDATA/apps/guide/
      methods: get # GET /upload returns file info as JSON

The list of files uploaded can be retrieved from the upload URL, along with associated information:

You can also retrieve the data in Python via FileUpload(path).info().

import gramex.handlers.uploadhandler
uploader = gramex.handlers.uploadhandler.FileUpload(path)
return uploader.info()

The uploader.info() is a list of info objects with the following keys:

Upload deletion

To delete a file, submit a POST request to the UploadHandler with a delete key. Here is a sample AJAX request:

$.ajax("upload", {
  method: "POST",
  data: { delete: "path/to/file.xlsx" }, // Must match 'file' in uploader.info()
});

Upload arguments

By default, UploadHandler uses these form keys:

You can change the keys used via the keys: configuration.

url:
  uploadhandler:
    pattern: ...
    handler: UploadHandler
    kwargs:
      path: ...
      keys: # Define what query parameters to use
        file: [file, upload] # Use <input id="file"> and/or <input id="upload">
        delete: [del, rm] # Use <input id="del"> and/or <input id="rm">
        save: [save] # Use <input id="save"> to specify the save location

To prevent users from changing or setting the filename, use:

keys:
  save: [] # No field names can override the user provided filename

Transform uploads

UploadHandler accepts a transform: function that is called after EACH file is saved. For example:

url:
  uploadhandler:
    pattern: ...
    handler: UploadHandler
    kwargs:
      path: ...
      transform:
        function: module.func(content, handler)

This calls module.func(content, handler) where

For example, this function will save CSV files as data.json:

def func(content, handler):
    if content.mime == 'text/csv':
        path = os.path.join('... upload path ...', content.file)
        pd.read_csv(path).to_json('data.json')

You can modify the file info before it is saved. For example:

def func(content, handler):
    # # Add a `year` field
    content['year'] = datetime.datetime.today().year
    # Remove `user` field if ?anonymize is passed
    if 'anonymize' in content['data']:
        del content['user']

You can modify the file info by returning a new dictionary too. For example:

def func(content, handler):
    # Only store the key and file, nothing else
    return {'key': content['key'], 'file': content['file']}

You can redirect after uploading. To upload a file and send the user to output?file=<filename>, use:

def func(content, handler):
    handler.set_status(status_code=302)
    handler.set_header("Location", f'output?file={content["file"]}')

If the user uploads multiple files, the last redirect is used.