I recently came across this question and answer https://stackoverflow.com/a/44926557/12322095 regarding Flask file uploads.
This worked perfectly fine until I uploaded an image with the same name again. It didn't change the image or overlayed it.
My question here is what if a user uploads an image with the same name, is there any way we could show an error message or maybe automatically change the name to something else.
For automatic changing the name, I researched and it can be done via resolve_conflict but I couldn't understand how to implement it.
my code is ditto as the the reference
You need to create some kind of uniuqe ID to append to the filename, before saving the file.
This can be done with something like:
from uuid import uuid4
def make_unique(string):
ident = uuid4().__str__()
return f"{ident}-{string}"
Which adds an unique UUID to the beginning of the string:
>>> make_unique('something.txt')
'f374b80c-b09e-468f-adc6-3d9df175cee7-something.txt'
To use this in the upload code, just run the filename through that function before you save. Be sure to put the filename through the secure_filename function first though:
if file and allowed_file(file.filename):
original_filename = secure_filename(file.filename)
unique_filename = make_unique(original_filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], unique_filename))
Although this works for the purpose of avoiding duplicates, in a larger application you may wish to extend this approach.
If you store the values of original_filename and unique_filename in the database, then this allows you to do the following in the download route:
from flask import send_file
# ...
f = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename)
send_file(f, attachment_filename=original_filename)
This has the advantage that the file is stored on your server with a unique identifier, but the user would never know this, as the file is served back to them with the originally uploaded filename.
In fact you may wish to go further, and simply save the file on your end with the UUID string, rather than appending the filename; instead of using the make_unique function above, change that third line to:
unique_filename = uuid4().__str__()
This will still serve the file with the correct mimetype, as send_file guesses the mimetype based on the provided attachment_filename.
Related
I am getting a file from the user and filter data from the model if the user uploaded file exists or not, but I am getting "None" while filtering.
from .models import SomeModel
uploaded_file = request.FILES.get('upload_file')
model_data = SomeModel.objects.filter(my_file=uploaded_file)
if model_data == None:
someModel.objects.create(my_file=uploaded_file)
else:
print('file already exist.')
print(model_data) # prints None
I have uploaded the same file too many times but it always creates new data in the model.
I know every time when uploaded file Django creates a new file in the media folder.
how can I filter if uploaded_file already exists in the model?
I would not suggest both of these approaches. Why?
Because multiple users may upload files with same name. Even same user can upload it multiple times with same name (So many users simply upload Untitles.xlsx.
Every time a file is uploaded, it is stored in a temporary location for the GET dictionary (try /tmp on linux). So your temporary name/location may be different if the same named file exists.
The intention might be to reupload the same file. In that case you might want to update.
So a safer option would be to get the checksum of the file (md5/sha1/sha256) and even store that in the database. That will tell you if the file is indeed the same.
You can check the currently uploaded file for checksum.
How do I access the tags attribute here in the Windows File Properties panel?
Are there any modules I can use? Most google searches yield properties related to media files, file access times, but not much related to metadata properties like Tags, Description etc.
the exif module was able to access a lot more properties than most of what I've been able to find, but still, it wasn't able to read the 'Tags' property.
The Description -> Tags property is what I want to read and write to a file.
There's an entire module dedicated to exactly what I wanted: IPTCInfo3.
import iptcinfo3, os, sys, random, string
# Random string gennerator
rnd = lambda length=3 : ''.join(random.choices(list(string.ascii_letters), k=length))
# Path to the file, open a IPTCInfo object
path = os.path.join(sys.path[0], 'DSC_7960.jpg')
info = iptcinfo3.IPTCInfo(path)
# Show the keywords
print(info['keywords'])
# Add a keyword and save
info['keywords'] = [rnd()]
info.save()
# Remove the weird ghost file created after saving
os.remove(path + '~')
I'm not particularly sure what the ghost file is or does, it looks to be an exact copy of the original file since the file size remains the same, but regardless, I remove it since it's completely useless to fulfilling the read/write purposes of metadata I need.
There have been some weird behaviours I've noticed while setting the keywords, like some get swallowed up into the file (the file size changed, I know they're there, but Windows doesn't acknowledge this), and only after manually deleting the keywords do they reappear suddenly. Very strange.
I'm starting with Django, so I'm pretty new to this. I'm sending a file to the Django server, saving it, trying to get the last file (the one I just send) from the saved folder and then I want to perform some processes on that file, after that I want to delete the original file. I already managed to upload and save the file to the filesystem, but I'm having trouble selecting this file from the folder.
I'm trying select and remove it this way:
#api_view(['POST', 'GET'])
def geojson_to_shape_view(request):
if request.method == 'POST':
serializer = FileSerializer(data=request.data)
if serializer.is_valid():
# serializer.save() calls .create() in serializers.py
file = serializer.save()
# Get last saved file
print("Analyzing last posted file...")
list_of_files = glob.glob('../media/temp_files/*')
print(list_of_files)
if list_of_files:
latest_file = max(list_of_files, key=os.path.getctime)
print("Deleting last posted file...")
try:
os.remove(latest_file)
pass
except:
print("Can't remove the file")
return Response(FileSerializer(file).data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
And this code returns:
Analyzing last posted file...
[]
I tried to run a script in the same folder with these file interactions and it run with no problems, so I thinking is some django permission issue. I also tried to change the path to settings.MEDIA_ROOT + 'temp_files/*', but the problem persists.
As already mentionned by AKX, your issue is here:
list_of_files = glob.glob('../media/temp_files/*')
Relative path are resolved against the current working directory, which can actually be just anything, so you need to use absolute path if you expect your code to work reliably.
But it isn't possible to work with relative paths?
Well, technically yes, but as I stated above - and as you already found out by yourself -, the final result is totally unpredictable. And no, changing the current working directory is not a solution - it's unreliable too (some other code - possibly in another thread - can change it back under your feet), and since it requires knowing the absolute path to switch to, it's much simpler to just build a proper absolute path.
NB: you may want to have a look at os.path and (python3.x) the path module for filesystem path manipulations.
I have just started working on web2py. Personally, I find it easier to learn than Django.
My query is that I have to load a file at application startup. Its a pickled hashtable. Where should I store this file so that the system is able to see it
My code is :
import cPickle as pickle
def index():
"""
Load the file into memory and message the number of entries
"""
f = open('tables.pkl','rb')
session.tables = pickle.load(f)
f.close()
terms = len(session.tables.keys())
message = 'The total entries in table = ' + str(terms)
return dict(message=message)
As you can see, I have put the code in index() to load it at startup. At present I am using the absolute path upto the physical location of the 'tables.pkl' file. Where should i put it in my application folder.
Also, I want tables variable to be available to all functions in the controller. Is session.tables the right way to go? It is just a search app so there is no user login.
The table has to be loaded only once for all users accessing the page.
Thank you.
I think the private folder would be a good place for this. You can get the absolute path with:
import os
fp = os.path.join(request.folder,'private','tables.pkl')
I would use cache instead of session if the file is not unique per user.
What's the best way to rename photos with a unique filename on the server as they are uploaded, using django? I want to make sure each name is used only once. Are there any pinax apps that can do this, perhaps with GUID?
Use uuid. To tie that into your model see Django documentation for FileField upload_to.
For example in your models.py define the following function:
import uuid
import os
def get_file_path(instance, filename):
ext = filename.split('.')[-1]
filename = "%s.%s" % (uuid.uuid4(), ext)
return os.path.join('uploads/logos', filename)
Then, when defining your FileField/ImageField, specify get_file_path as the upload_to value.
file = models.FileField(upload_to=get_file_path,
null=True,
blank=True,
verbose_name=_(u'Contact list'))
A better way could be using a common class in your helpers.py. This way you could reuse the random file generator across your apps.
In your helpers.py:
import os
import uuid
from django.utils.deconstruct import deconstructible
#deconstructible
class RandomFileName(object):
def __init__(self, path):
self.path = os.path.join(path, "%s%s")
def __call__(self, _, filename):
# #note It's up to the validators to check if it's the correct file type in name or if one even exist.
extension = os.path.splitext(filename)[1]
return self.path % (uuid.uuid4(), extension)
And then in your model just import the helper class:
from mymodule.helpers import RandomFileName
And then use it:
logo = models.ImageField(upload_to=RandomFileName('logos'))
Ref: https://coderwall.com/p/hfgoiw/give-imagefield-uploads-a-unique-name-to-avoid-file-overwrites
As of the writing of this answer it seems like you no longer need to do anything special to make this happen. If you set up a FileField with a static upload_to property, the Django storage system will automatically manage naming so that if a duplicate filename is uploaded, Django will randomly generate a new unique filename for the duplicate.
Works on Django 1.10.
Prior to Django 1.6.6, 1.5.9, and 1.4.14, the get_avaialable_name function would automatically give files a unique name by adding an underscore. So, for example, if you save one file "test.jpg" and then another file, "test.jpg" to your server, the first will be called test.jpg, and the second will be called test_1.jpg.
Alas, that turns out to be a vector for DDOSing a machine, by sending it thousands of zero-byte files to store, each one checking thousands of previous files to see what its name should be.
As you'll see in the docs, the new system appends seven random digits after the underscore to fix this problem.
You can write your own FileField and override generate_filename.
For example:
class UniqueNameFileField(FileField):
def generate_filename(self, instance, filename):
_, ext = os.path.splitext(filename)
name = f'{uuid.uuid4().hex}{ext}'
return super().generate_filename(instance, name)
How about concatenating the filename with the date / time the photo was uploaded and then using hashlib to create a message digest? That should give you unique filenames.
Alternatively you could re-use a neat little snippet which creates unique filenames and then use the full path to that file as the input to your hash call. That gives you unique constant length strings which you can map to your files.
django enforce unique filename automatically.
if the file already exists, seven unique characters are appended to the filename
tested on django 2.2