Uploading files
There are two ways for the user to upload a file in the VIKTOR interface:
- By defining a
FileField
in the parametrization, a user can upload a file through an upload modal and directly attach it to a specific field. This way, the developer can easily use the file (content) which will be present in theparams
of a job. This method is encouraged due to its simplicity and the possibility for users to stay within the current entity in which theFileField
resides. - By creating a file-like entity type. When a user creates a new entity of this type, he or she will be prompted with an upload modal instead of just the entity name. This method is a bit more cumbersome for users, since it requires more actions (clicks) to be performed and navigation between entities. This method can be used to isolate a file with, for example, attached views.
Upload using FileField
Adding a FileField
(or
MultiFileField
) is as easy as adding any other field:
import viktor as vkt
class Parametrization(vkt.Parametrization):
csv_file = vkt.FileField('CSV file')
These fields will be generated in the VIKTOR interface as dropdown fields, with the possibility to upload one or
multiple files. When the user has made a selection, the file resource will be available in the params
of every job as
FileResource
object:
In this example we use the .file
property to get the file contents of the uploaded file as a VIKTOR File
object. You
can also use the .filename
property to get the file name, these two properties make up a VIKTOR File
object. After that
you can use it as a regular File
object in your app, for example by loading the csv in a pandas DataFrame and
visualizing it in a TableView
.
import viktor as vkt
import pandas as pd
class Parametrization(vkt.Parametrization):
csv_file = vkt.FileField('CSV file')
class Controller(vkt.Controller):
parametrization = Parametrization
@vkt.TableView("Table")
def show_csv(self, params, **kwargs):
csv_file = params.csv_file.file # Retrieve the File object from the FileResource
with csv_file.open() as r:
df = pd.read_csv(r)
return vkt.TableResult(df)
FileResource
objects originating from a FileField
or MultiFileField
are
memoizable
Temporary files
Although not recommended, it is possible to use temporary files.
This module creates temporary files that get deleted after closing.
You can use this as a temporary storage solution as the file is
created securely and on completion is removed. For a NamedTemporaryFile
the returned object is always a file-like object that can be used in
a with
statement, just like a normal file.
Upload using file-like entity type
In short, the following steps should be implemented. A more elaborate example is shown in the following sections.
-
Add a new entity type by defining a controller.
-
Implement a process method (can be named arbitrarily) on the controller class, decorated by
ParamsFromFile
.import viktor as vkt
class Controller(vkt.Controller):
@vkt.ParamsFromFile()
def process_file(self, file: vkt.File, **kwargs):
return {} # nothing to store
In the example above, the process_file
method does not contain any logic. However, this method can also be used to
parse certain information from the file, which can be stored in the parametrization of the entity. See the example
below if this is desired.
File processing
Prevent storing the complete file content on the properties if the file is large, as this may cause speed and/or stability issues. The file content can be retrieved at all times using the API whenever necessary.
The processed content can only be stored on fields that are defined in the entity's parametrization. These can be
input fields (e.g. NumberField
), but also read-only
(OutputField
) or even a
HiddenField
. Let's assume the following parametrization
and text file to be uploaded:
class Parametrization(vkt.Parametrization):
number_of_entries = vkt.NumberField('Number of entries')
project_name = vkt.TextField('Project')
Project=Super nice project
Entry1
Entry2
Entry3
The decorated process method should return the data in a dictionary format. The structure of this dictionary should match with the structure of the parametrization fields:
import viktor as vkt
class Controller(vkt.Controller):
@vkt.ParamsFromFile()
def process_file(self, file: vkt.File, **kwargs): # viktor.core.File
# app specific parse logic
number_of_entries = 0
project_name = ''
with file.open(encoding='utf-8') as f:
for lineno, line in enumerate(f):
if lineno == 0:
_, project_name = line.split('=')
elif line.startswith('Entry'):
number_of_entries += 1
# linking the parsed output to the parametrization fields (names in database)
return {
'number_of_entries': number_of_entries,
'project_name': project_name
}
In case of a nested parametrization structure, the return value is a nested dictionary:
class Controller(vkt.Controller):
@vkt.ParamsFromFile()
def process_file(self, file: vkt.File, **kwargs):
...
return {
'tab': {
'section' : {
'number_of_entries': number_of_entries,
'project_name': project_name
}
}
}
The file entity can be retrieved and the file (content) can be extracted (from either the current entity or another) using the API.
File restrictions
A limit is set on the file size of an upload to protect the developer for potential memory issues and/or instability of
the application when handled incorrectly. Sometimes however, it is required to deal with large files. In this case the
limit can be increased using the max_size
argument:
file = vkt.FileField('CPT file', max_size=100_000_000) # 100 MB
@vkt.ParamsFromFile(max_size=100_000_000) # 100 MB
Keep in mind that reading the complete content of a large file in memory will lead to memory issues. We therefore
recommend reading the file in chunks (see File
).
Similarly, you are able to restrict the file type(s) that may be uploaded, by means of file_types
:
file = vkt.FileField('CPT file', file_types=['.png', '.jpg', '.jpeg'])
@vkt.ParamsFromFile(file_types=['.png', '.jpg', '.jpeg'])
Excel vs CSV
If you want to upload an Excel file, the preferred method is to convert it to a plain-text CSV (comma-separated values) format (extension ".csv"). Excel files (".xls", ".xlsx") are not plain-text and special characters are not always imported correctly. An additional advantage of a CSV file is that, being plain-text, they can be compared using version control software (e.g. Git).
A CSV file does not support 'sheets' as an Excel file does, meaning that a CSV file must be created for each sheet in a multi-sheet Excel file.