Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

File input handling #373

Answered by rmorshea
ksmoore17 asked this question in Question
Discussion options

Hi, this package is so cool.

I'm trying to use an input to get an image from the client into bytes (or anything I can open with PIL on the server side). I've tried to use the Input widget with type='file', and the resulting argument to the callback is a fakepath. I can also see that value is the only key in the event dictionary with the same fakepath when I create a handler manually and use idol.html.input. From my limited js knowledge, the fakepath is not useful and a bytes array from event['target']['result'] is what I was hoping for.

@events.on("change")
def on_change(event: Dict[str, Any]) -> None:
 value = event['target']['result']
 update_image_bytes(value)
return idom.html.input(
 {
 'id': 'image-input',
 'type': 'file',
 'hidden': True,
 'accept': 'image/*'
 },
 event_handlers=events
)

I also think that I could do it if I could access the input like document.getElementById("#image-input").files[0], but I don't know anything about that in idom. I guess that another possibility would be to use a js component here.

To be clear, I don't really want to post/upload, I'm basically just trying to display the file after being b64 encoded and used as src in an image element (I have been able to get this encoding and displaying working in js). Any help would be greatly appreciated.

You must be logged in to vote

I've created an issue to track this: #375

Replies: 1 comment 7 replies

Comment options

I've created an issue to track this: #375

You must be logged in to vote
7 replies
Comment options

I'm on main so maybe there's a difference, but I'm actually getting a files key, it's just not serializing correctly. The following code:

import idom
@idom.component
def Test():
 return idom.html.input({"type": "file", "id": "my-input", "onChange": print})
idom.run(Test, port=8000)

Produces the following output when a file is added:

{'value': 'C:\\fakepath\\some-file.txt', 'files': {'0': {}}}
Comment options

@ksmoore17 I think the reason you're not seeing the latest updates is due to a bug in how the JS build dir is updated. You should get the same output as me if you run idom restore. This will be fixed in the next release.

Comment options

Seeing that files key now! And with a dict representation of the file object. Realizing now that I will need to use the js FileReader api or something similar (File.arrayBuffer()) since the python side can't really work with the files from the "handles" (the dicts returned in the files key). I tried for a bit to return it in those dicts in the hasFiles callback (tacking it on as a string). This was the general idea:

files: Array.from(target.files).map((file) => {
 var bytes;
 file.arrayBuffer().then((buffer) => {bytes = buffer});
 return {
 lastModified: file.lastModified,
 name: file.name,
 size: file.size,
 type: file.type,
 bytes: bytes
 }
})

This seems like a dead end because it's an async process, and it was supposed to be a hack anyway. I think it would be nice to have a python idom.util function or something like a useFile hook that can take this file handle that is being successfully sent to the python event handler and access the FileReader api on the browser to send back the bytes array. Let me know what you think, I would be interested in working on this.

Comment options

I think this might be best written as a custom component, the API for which has been subject to change in the past. However I think it'll be mostly finalized in the next release so once it's out I can make the necessary changes and remove the warning from the template repository.

Comment options

I can imagine the JS module might look like this:

export function createElement() {};
export function renderElement() {};
export function unmountElement() {};
export function FileInput({ onChange }) {
 const handleChange = (event) => {
 const fileDataPromises = Array.from(event.target.files).map(promiseFileData);
 Promise.all(fileDataPromises).then((files) => onChange({ files: files });
 };
 return <input onChange={handleChange} />
} 
function promiseFileData(file) {
 return file.arrayBuffer().then((bytes) => (
 {
 lastModified: file.lastModified,
 name: file.name,
 size: file.size,
 type: file.type,
 bytes: bytes
 }
 ))
}
Answer selected by rmorshea
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /