django-imagekit - do not crash when source file does not exist - django

I have a Django model that has an image and an autogenerated thumbnail:
img = ImageField(upload_to=..., max_length=64)
img_thumbnail = ImageSpecField([ResizeToFit(width=64, height=64)], source='img')
For a number of reasons, the img field may contain a filename that is no longer present in the filesystem. When the file is not there, the view crashes with a FileNotFoundError - No such file or directory: '....png'
Is it possible to configure the img_thumbnail field / django-imagekit to fail silently? Also without writing having to catch the exception in the view.

Related

NamedTemporaryFile triggers SuspiciousFileOperation

In django version 3.2.12, I try to download an image for which I have an URL and store it inside an ImageField. It looks like this:
def load_image_from_url(self):
image_file = NamedTemporaryFile(delete=True, dir='project/media')
with urlopen(self.extra_large_url) as uo:
assert uo.status == 200
image_file.write(uo.read())
image_file.flush()
image = File(image_file)
self.image.save(image.name, image)
When I create the NamedTemporaryFile, the path generated is an absolute path.
In django.core.files.utils, it get blocked in this condition:
if allow_relative_path:
# Use PurePosixPath() because this branch is checked only in
# FileField.generate_filename() where all file paths are expected to be
# Unix style (with forward slashes).
path = pathlib.PurePosixPath(name)
if path.is_absolute() or '..' in path.parts:
raise SuspiciousFileOperation(
"Detected path traversal attempt in '%s'" % name
)
With a dir argument set for the NamedTemporaryFile, the issue remains even if the folder is a subdirectory of the project.
Is there a better solution than doing this:
self.image.save(os.path.basename(image.name), image)

Django, Store jpg file received as string in http POST

I am receiving an http request from a desktop application with a screenshot. I cannot speak with the developer or see source code, so all I have is the http request I am getting.
The file isn't in request.FILES, it is in request.POST.
#csrf_exempt
def create_contract_event_handler(request, contract_id, event_type):
keyboard_events_count = request.POST.get('keyboard_events_count')
mouse_events_count = request.POST.get('mouse_events_count')
screenshot_file = request.POST.get('screenshot_file')
barr2 = bytes(screenshot_file.encode(encoding='utf8'))
with open('.test/output.jpeg', 'wb') as f:
f.write(barr2)
f.close()
The file is corrupted.
The binary starts like this, I don't know if that helps:
����JFIFHH��C
%# , #&')*)-0-(0%()(��C
(((((((((((((((((((((((((((((((((((((((((((((((((((�� `"��
Also, if I try to open the image with PIL, I get the following error:
from PIL import Image
im = Image.open('./test/output.jpg')
#OSError: cannot identify image file './test/output.jpg'
Finally, I managed to touch the code in the other hand, the 'filename' was missing in the header and for that reason I was getting the file in the POST instead of in the FILES dictionary.

Django: No such file or directory

I have a process that scans a tape library and looks for media that has expired, so they can be removed and reused before sending the tapes to an offsite vault. (We have some 7 day policies that never make it offsite.) This process takes around 20 minutes to run, so I didn't want it to run on-demand when loading/refreshing the page. Rather, I set up a django-cron job (I know I could have done this in Linux cron, but wanted the project to be as self-contained as possible) to run the scan, and creates a file in /tmp. I've verified that this works -- the file exists in /tmp from this morning's execution. The problem I'm having is that now I want to display a list of those expired (scratch) media on my web page, but the script is saying that it can't find the file. When the file was created, I use the absolute filename "/tmp/scratch.2015-11-13.out" (for example), but here's the error I get in the browser:
IOError at /
[Errno 2] No such file or directory: '/tmp/corpscratch.2015-11-13.out'
My assumption is that this is a "web root" issue, but I just can't figure it out. I tried copying the file to the /static/ and /media/ directories configured in django, and even in the django root directory, and the project root directory, but nothing seems to work. When it says it cant' find /tmp/file, where is it really looking?
def sample():
""" Just testing """
today = datetime.date.today() #format 2015-11-31
inputfile = "/tmp/corpscratch.%s.out" % str(today)
with open(inputfile) as fh: # This is the line reporting the error
lines = [line.strip('\n') for line in fh]
print(lines)
The print statement was used for testing in the shell (which works, I might add), but the browser gives an error.
And the file does exist:
$ ls /tmp/corpscratch.2015-11-13.out
/tmp/corpscratch.2015-11-13.out
Thanks.
Edit: was mistaken, doesn't work in python shell either. Was thinking of a previous issue.
Use this instead:
today = datetime.datetime.today().date()
inputfile = "/tmp/corpscratch.%s.out" % str(today)
Or:
today = datetime.datetime.today().strftime('%Y-%m-%d')
inputfile = "/tmp/corpscratch.%s.out" % today # No need to use str()
See the difference:
>>> str(datetime.datetime.today().date())
'2015-11-13'
>>> str(datetime.datetime.today())
'2015-11-13 15:56:19.578569'
I ended up finding this elsewhere:
today = datetime.date.today() #format 2015-11-31
inputfilename = "tmp/corpscratch.%s.out" % str(today)
inputfile = os.path.join(settings.PROJECT_ROOT, inputfilename)
With settings.py containing the following:
PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
Completely resolved my issues.

How to customize TRAC plugin template python file

I am currently modifying our TRAC instance to Bootstrap 3.1. However, some templating needs to be done on the .py files. I only know how to customize .html files... just add classes, customize DOM structure a little bit then put it in templates folder of our TRAC instance.
NOW WHAT ABOUT customizing .py files from plugins? I tried putting them in templates folder but nothing happened.
I had no experience with Python, but it's easy just to hack around and add a bootstrap class e.g adding "col-sm-2 control-label" in a label in milestone.py
def __edit_project(self, data, req):
milestone = data.get('milestone').name
all_projects = self.__SmpModel.get_all_projects_filtered_by_conditions(req)
id_project_milestone = self.__SmpModel.get_id_project_milestone(milestone)
if id_project_milestone != None:
id_project_selected = id_project_milestone[0]
else:
id_project_selected = None
return tag.div(
tag.label(
class_="col-sm-2 control-label",
'Project',
tag.br(),
tag.select(
tag.option(),
[tag.option(row[1], selected=(id_project_selected == row[0] or None), value=row[0]) for row in sorted(all_projects, key=itemgetter(1))],
name="project")
),
class_="field")
Compiling the plugin again worked for me. After adding bootstrap classes on specific .py files, here are the steps/commands I did:
In our TRAC environment plugins directory where specific setup.py of the plugin I'm editing is located, build the .egg file e.g
tracproject/plugins_source/sampleplugin: python setup.py bdist_egg
Then I renamed the plugin's original .egg file in the plugins directory e.g
tracproject/plugins/sampleplugin/: mv sampleplugin.egg sampleplugin.egg.old
After that, I copied the newly .egg file generated to the plugins directory e.g
tracproject/plugins_source/sampleplugin/dist: mv sampleplugin.egg ../../../plugins/
Lastly, I restarted our server e.g (however, there were cases, no restart was needed since changes were instantly reflected)
sudo service apache2 restart
Thanks #falkb! I see that you're the author of SimpleMultiProject plugin I was trying to put bootstrap classes. :)
Here's a snippet of simplemultiprojectplugin milestone.py where I added styling
def __edit_project(self, data, req):
milestone = data.get('milestone').name
all_projects = self.__SmpModel.get_all_projects_filtered_by_conditions(req)
id_project_milestone = self.__SmpModel.get_id_project_milestone(milestone)
if id_project_milestone != None:
id_project_selected = id_project_milestone[0]
else:
id_project_selected = None
return tag.div(
tag.label('Project', class_="control-label col-sm-2"),
tag.div(
tag.select(
tag.option(),
[tag.option(row[1], selected=(id_project_selected == row[0] or None), value=row[0]) for row in sorted(all_projects, key=itemgetter(1))],
name="project",
class_="form-control"),
class_="col-sm-5"),
class_="form-group")

How can I get the temporary name of an UploadedFile in Django?

I'm doing some file validation and want to load an UploadedFile into an external library while it is in the '/tmp' directory before I save it somewhere that it can be executed. Django does the following:
Django will write the uploaded file to a temporary file stored in your system's temporary directory. On a Unix-like platform this means you can expect Django to generate a file called something like /tmp/tmpzfp6I6.upload.
It ihe "tmpzfp616.upload' that I want to be able to get my hands on. UploadedFile.name gives me "" while file.name gives me the proper name of the file "example.mp3".
With the library I am using, I need to pass the filepath of the temporary file to the library, rather than the file itself and so, need the string.
Any ideas?
Thanks in advance.
EDIT: Here's my code:
from django.core.files.uploadedfile import UploadedFile
class SongForm(forms.ModelForm):
def clean_audio_file(self):
file = self.cleaned_data.get('audio_file',False)
if file:
[...]
if file._size > 2.5*1024*1024:
try:
#The following two lines are where I'm having trouble, MP3 takes the path to file as input.
path = UploadedFile.temporary_file_path
audio = MP3('%s' %path)
except HeaderNotFoundError:
raise forms.ValidationError("Cannot read file")
else:
raise forms.ValidationError("Couldn't read uploaded file")
return file
Using "UploadedFile" I get an AttributeError "type object 'UploadedFile' has no attribute 'temporary_file_path'". If I instead use file.temporary_file_path (just throwing darts in the dark here) I get an IOError:
[Errno 2] No such file or directory: 'bound method TemporaryUploadedFile.temporary_file_path of >'
I realize temporary_file_path is the solution I'm looking for, I just can't figure out how to use it and neither the docs nor google seem to be much help in this particular instance.
UploadedFile.temporary_file_path
Only files uploaded onto disk will have this method; it returns the full path to the temporary uploaded file.