Creating File with a BytesIO input

Description of the limitation and why it is relevant to address

I would like to make a Viktor File using BytesIO input and to be able to store on the server using Storage(). At this moment it is very difficult to store Excel filled template files on the server using Storage().

Filled template is produced only as a BytesIO object:

File object from_data only accepts strings or bytes:

Storage only accepts Viktor File objects as an input:

I think this is relevant for the VIKTOR platform because a user may want to store a wide range of file types in the server or even python objects. Limiting this to strings or bytes is quite restrictive.

Submitter proposed design (optional)

  • Make it possible to get filled templates from Excel as a Viktor File object (Excel().filled_template(as_file=True) [default false])
  • Make it possible to make Viktor File objects with more than just str and bytes or at least add BytesIO to this list.
  • Perhaps accept other file types to the storage

Current workarounds

In the case of the Excel filled template we haven’t (yet) been able to find a workaround. Just converting a BytesIO to bytes using .read() isn’t doing the trick.

Hi Enrique,

Thanks for posting.

First of all, very good suggestion to make the VIKTOR file object as an optional output of the filled_template method. :+1:

On the topic of current workaround/solution to this problem:
It is really easy to get bytes from a BytesIO object, since a BytesIO is just a in-memory buffer for bytes.
The BytesIO object has a method called `getvalues’ that returns the bytes from the buffer.

so this should work: File.from_data(bytes_io_object.getvalue())

I hope this solves your problem.

1 Like

Hi Enrique,

Just one small comment on the .read() method. This one is tricky. The BytesIO buffer has a sort of internal ‘cursor’. The read() method returns the bytes from the cursor until the end of the buffer (depending on the argument given). After writing to the buffer, the cursor is at the end of the buffer. So the read() function returns an empty bytes string. This is why it did not work for you.

The cursor can be (re)set using the .seek() method. .seek(0) will set it to the beginning of the buffer.

So first calling .seek(0) and then read() will give you the correct bytes string.

Basically, .readvalues() does this for you in a single method.

Thank you very much for the explanation :slight_smile: