I only want the file_uploader to run the callback function when I remove any file or upload any new file to the file_uploader widget. But now I’m having this issue where it keeps calling the ‘on_change’ callback function even though I didn’t remove or upload any file. The way to trigger this is just to change any other widget’s value (e.g. the st.radio button in the example code below).
But I know I’m actually changing it’s value when I tried to access it to copy the file to a temporary folder to use it to do something. So I tried to use video_file.seek(0) and also deepcopy(video_file) to try to avoid changing the value of the file_uploader, but still in vain. Please help.
import streamlit as st
import shutil
import os
from copy import deepcopy
from pathlib import Path
TEMP_DIR = Path('tmp/extracted')
def dummy_cb():
print("DUMMY CALLBACK FOR FILE UPLOADER")
video_file = st.sidebar.file_uploader(
"Upload a video", type=['mp4', 'mov', 'avi', 'asf', 'm4v'],
key='video_file_uploader', on_change=dummy_cb)
if video_file is None:
st.stop()
# use this to avoid keep calling the file_uploader's callback
# NOTE: still not helping...
uploaded_video = deepcopy(video_file)
video_path = str(TEMP_DIR / uploaded_video.name)
print("Copying file")
if TEMP_DIR.exists():
shutil.rmtree(TEMP_DIR)
os.makedirs(TEMP_DIR)
print(f"{video_path = }")
with open(video_path, 'wb') as f:
f.write(uploaded_video.getvalue())
# seeking is also not helping
uploaded_video.seek(0)
video_file.seek(0)
st.radio("options", (1, 2), key='dummy_options')
st.stop()
I settled with a workaround by performing all the necessary callbacks within the if video_file is None statement. This works well for accepting a single upload but probably not multiple files.
if video_file is None:
logger.info("Resetting states for uploaded video")
# reset and remove the TEMP_DIR if there's no uploaded file
# to ensure the app only reads the new uploaded file
reset_cb()
if TEMP_DIR.exists():
shutil.rmtree(TEMP_DIR)
os.makedirs(TEMP_DIR)
st.stop()
# using str to ensure readable by st.video and cv2
video_path = str(TEMP_DIR / video_file.name)
if not os.path.exists(video_path):
# only creates it if not exists, to speed up the process when
# there is any widget changes besides the st.file_uploader
logger.debug(f"{video_path = }")
with st.spinner("Copying video to a temporary directory ..."):
with open(video_path, 'wb') as f:
f.write(video_file.getvalue())
Hi, I am having the same issue. I’ve created a demo app to replicate the issue so that it can hopefully be addressed sooner.
import streamlit as st
def uploader_callback():
print('Uploaded file')
st.file_uploader(
label='File uploader',
on_change=uploader_callback,
key='file_uploader'
)
st.text_input(label='Textbox 1', key='first')
Bug: If another widget gets updated, e.g. st.text_input, the callback function passed into ‘on_change’ of st.file_uploader gets reran even if the file has remained the same.
Desired: st.file_uploader on_change to behave as it should - only running when the value of st.file_uploader is changed
I also tried the on_change callback of the st.file_uploader widget and found that the callback function is called twice for each upload, except for the very first time.
Is this intentional?
@nick_muchi My understanding of this behavior is that each subsequent upload also clears the existing upload. Clearing the existing upload triggers the callback, too. For each new upload, the callback is called when the existing upload is cleared AND when the new file is uploaded.
A workaround is to execute the callback if the contents of the uploaded file is not None. Doing so ensures the callback contents are not executed when the existing upload is automatically cleared during a new upload. Here’s an example of the workaround:
import streamlit as st
if 'ctr' not in st.session_state:
st.session_state['ctr'] = 0
def uploader_callback():
if st.session_state['file_uploader'] is not None:
st.session_state['ctr'] += 1
print('Uploaded file #%d' % st.session_state['ctr'])
st.file_uploader(
label="File uploader", on_change=uploader_callback, key="file_uploader"
)
Thanks for your help, I think my issue is a bit different but related to the file_uploader callback, hope you can assist on that if you have time. I tried but could not come up with a workaround.
If you feel strongly about it, please consider upvoting the GitHub issue and commenting with your support to bring the issue to the attention of our devs:
Thanks for stopping by! We use cookies to help us understand how you interact with our website.
By clicking “Accept all”, you consent to our use of cookies. For more information, please see our privacy policy.
Cookie settings
Strictly necessary cookies
These cookies are necessary for the website to function and cannot be switched off. They are usually only set in response to actions made by you which amount to a request for services, such as setting your privacy preferences, logging in or filling in forms.
Performance cookies
These cookies allow us to count visits and traffic sources so we can measure and improve the performance of our site. They help us understand how visitors move around the site and which pages are most frequently visited.
Functional cookies
These cookies are used to record your choices and settings, maintain your preferences over time and recognize you when you return to our website. These cookies help us to personalize our content for you and remember your preferences.
Targeting cookies
These cookies may be deployed to our site by our advertising partners to build a profile of your interest and provide you with content that is relevant to you, including showing you relevant ads on other websites.