My local development and live development server have the exact same code and database (all static/media files upload to s3 storage).
I have a Post model with an image field. My Post has an image upload field which uploads via AJAX to my s3 storage. In both my local and live server, the image successfully uploads and the url of that image in my s3 bucket is saved. So when I submit the Post in my local server, it doesn't upload again, I just point to the URL when I render the image.
However, my remote server doesn't do this. After I have uploaded the image via AJAX, when I submit the Post form, it then submits the image again (it shows the upload progression on my browser - 3%, 5%, 10% etc...).
Any idea why this happens? My code is here:
models
class Post(models.Model):
image = models.FileField(null=True, blank=True)
views
def post(request):
if request.user.is_authenticated():
form_post = PostForm(request.POST or None, request.FILES or None)
if form_post.is_valid():
instance = form_post.save(commit=False)
instance.user = request.user
...
if instance.image:
instance.imageURL = "https://s3.us-east-2.amazonaws.com/my-bucket/media/%s" % filename
instance.image = None #this prevents re-upload upon form submission
instance.save()
return HttpResponseRedirect('/')
else:
form_post = PostForm()
context = {
'form_post': form_post,
}
return render(request, 'post/post.html', context)
else:
return HttpResponseRedirect("/accounts/signup/")
ajax view
def upload_image(request):
random_filename = request.POST.get('random_filename')
if request.is_ajax():
if request.FILES:
img = request.FILES.get('image')
session = boto3.Session(
aws_access_key_id=AWS_ACCESS_KEY_ID,
aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
)
s3 = session.resource('s3')
s3.Bucket('my-bucket').put_object(Key='media/%s' % random_filename, Body=img)
return HttpResponse()
else:
return HttpResponse()
template
<input id="id_image" type="file" name="image" />
JS
$('input#id_image').on('change', function(e) {
var file = $('input[type=file]')[0].files[0];
var formData = new FormData();
$('.add_file_div h3').hide();
$('.add_file_label').css({
'border-radius': '5px',
'box-shadow': '5px',
});
var imagePath = URL.createObjectURL(e.target.files[0]);
var random_filename = random_string();
formData.append('image', file);
formData.append('csrfmiddlewaretoken', $("input[name='csrfmiddlewaretoken']").val());
formData.append('random_filename', random_filename);
$.ajax({
url: '/upload_image/',
data: formData,
type: 'POST',
contentType: false,
processData: false,
success: function(){
console.log('SUCCESSFULLY UPLOADED');
$('.add_file_label').css('opacity', '1');
$('.add_file_label').css('background', 'url(' + imagePath + ') scroll no-repeat center/cover');
$('.loading_spinner').css('visibility', 'hidden');
}
});
$('.add_file_label').css('background', 'url(' + imagePath + ') scroll no-repeat center/cover');
$('.add_file_label').css('opacity', '0.4');
$('.loading_spinner').css('visibility', 'visible');
console.log(imagePath);
$('.add_file_label').append('<input class="temp_s3_url" type="hidden" name="temp_s3_url">');
$('.temp_s3_url').val(random_filename);
$('input#id_imageURL').hide();
$('#delete_preview_image').show();
});
PS: My nginx has client_max_body_size 200m; so that's not the problem
I think the same issue may be happening locally also. But when you are local the speed is fast enough for you to not see a upload progress bar. The thing is that you should clear your file field when you have had a success in the ajax upload
how to reset <input type = "file">
so inside
success: function(){
console.log('SUCCESSFULLY UPLOADED');
$('.add_file_label').css('opacity', '1');
$('.add_file_label').css('background', 'url(' + imagePath + ') scroll no-repeat center/cover');
$('.loading_spinner').css('visibility', 'hidden');
}
You would reset the file element to make it a blank value, else it will get submitted with the form.
Another option could be to just give the file input field a id and not a name. If there is no name attached to a input element it doesn't get submitted with a form
Related
I am trying to build a Flask application to upload files to a GS bucket. I ran app.py on localhost, and when I try to submit the files, the server raises the 405 Method Not Allowed error. I searched everywhere, and nothing seems to help. Here is my code:
HTML Form:
<form action="/" method="POST" enctype="multipart/form-data">
<input
class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-50 dark:text-gray-400 focus:outline-none dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400"
id="multiple_files" type="file" multiple>
app.js:
document.getElementById("request").addEventListener("click", function (event) {
event.preventDefault();
const files = document.getElementById("multiple_files").files;
const formData = new FormData();
for (let i = 0; i < files.length; i++) {
formData.append("multiple_files", files[i]);
}
fetch("/", {
method: "POST",
headers: {
'Access-Control-Allow-Origin': '*'
},
body: formData
})
.then(response => response.json())
.then(data => {
console.log(data);
})
.catch(error => {
console.error(error);
});
});
app.py
import os
import uuid
from flask import Flask, request, redirect, render_template
from google.cloud import storage
app = Flask(__name__)
# Set the bucket name and json authentication file values
bucket_name = "BUCKET_NAME"
auth_file = "AUTH_FILE"
# Initialize Google Cloud Storage client
client = storage.Client.from_service_account_json(auth_file)
bucket = client.get_bucket(bucket_name)
# Route for the file upload form
#app.route("/", methods=["GET", "POST"])
def index():
if request.method == "POST":
# Get the uploaded files
files = request.files.getlist("multiple_files")
# Upload each file to the Google Cloud Storage bucket
for file in files:
# Generate a unique file name
filename = str(uuid.uuid4()) + "-" + file.filename
blob = bucket.blob(filename)
blob.upload_from_file(file)
return redirect("/")
return render_template("index.html")
if __name__ == "__main__":
app.run(debug=True)
Can you please help me? I just started learning web development, and I cannot find any resource to help me fix this. Thanks!
I tried to change the "POST" to "PUT" but it also did not help.
I need a protected video file on a page rendered by Django. The file is protected, but it's not serving an html rendered page with the <video src="..."> as I'd expect, like netflix. Instead, all I get is a jumbled mess like this image.
I know the internal redirect is serving the file, therefore it shows up like that, but I need it on a rendered page with the other html like netflix does.... What am I doing wrong??
Nginx conf file:
location /secret_videos/ {
internal;
alias /home/username/path/to/secret/videos/;
}
Url:
path('protected_video/', views.protected_video, name='protected_video'),
View:
def protected_video(request):
....
if request.method =='POST':
if some_var == 'the_correct_value':
protected_uri = '/secret_videos/secret-vid-1.mp4'
response = render(request, 'template.html', {'some_var ': True, 'protected_uri': protected_uri})
response['X-Accel-Redirect'] = protected_uri
return response
return render(request, 'template.html', {})
Template, but it's not rendering html, only the image above:
<video width="75%" height="auto" controls>
<source src="{{ protected_uri }}" type="video/mp4" />
Your browser doesn't support the mp4 video format.
</video>
You're combining what should be two request/responses: rendering the page, and sending the video.
You need to render the template, in which you give the video a URL that calls a Django view. That second view then returns a response with the secret URL as the accel-redirect. So:
path('protected_video/', views.protected_video, name='protected_video'),
path('video_url/<slug: video_slug>/', views.redirect_to_video, name='redirect_to_video'),
...
def protected_video(request):
....
if request.method =='POST':
if some_var == 'the_correct_value':
protected_uri = reverse('redirect_to_video' , kwargs={'video_slug': 'some_slug'})
return render(request, 'template.html', {'some_var ': True, 'protected_uri': protected_uri})
def redirect_to_video(request, slug):
... some logic to get the secret URL from the slug ...
response = HttpResponse()
response['X-Accel-Redirect'] = secret_url
return response
Trying to upload an image to a model image field with AJAX. Here's my code:
js
$('input#id_banner_image').on('change', function(e) {
$.ajax({
type: 'POST',
url: '/change_banner_image/',
data: {
image: URL.createObjectURL(e.target.files[0]),
csrfmiddlewaretoken: $("input[name='csrfmiddlewaretoken']").val()
}
})
});
views
def change_banner_image(request):
if request.is_ajax():
print(request.FILES) #prints <MultiValueDict: {}>
for i in request.FILES:
print(i) #prints nothing
image = request.FILES['id_banner_image'].name #this is wrong
profile = get_object_or_404(Profile, user=request.user)
profile.image = image
profile.save()
return HttpResponse()
How do I correctly grab the image in my views? I know that's the bit that's wrong.
I'm trying to upload the image file to the Media url specified in my setting.py and
store the path of an image in the database table.
However, I could not achieve this when using Ajax for uploading the image file..
template.html
<div class="profpic" style="background:url(../../../static/app/img/test.png);background-size:cover">
<input type="file" id="picpath" name="picpath" class="uploadpic" value=""/>
</div>
Ajax :
function saveprof() {
$.ajax({
type: "POST",
url: "saveprof",
enctype: 'multipart/form-data',
async: true,
data: {
'picpath_Aj': $('#picpath').val(),
'profemail_Aj': $('#profemail').val(),
'csrfmiddlewaretoken': $("input[name=csrfmiddlewaretoken]").val()
},
success: function (data, textStatus, jqXHR) {
$('#message').html(data);
}
});
}
Views.py
def saveprof(request):
if request.method == "POST":
picpathV = request.POST['picpath_Aj']
else:
profemailV = ''
response_data = 'Nothing to update!'
return HttpResponse(response_data, content_type="text/plain")
response_data = 'Empty'
try:
res=td_table.objects.filter(id=request.session.get('proid')).update(picpath=picpathV)
except:
response_data = 'Something went wrong!'
return HttpResponse(response_data, content_type="text/plain")
Above code is working fine, but I could save only the file path like ('C:\fakepath\file.jpg').. and file is not getting saved to the media
path provided in the Settings.py.
I could upload the file when I use request.FILES in the view, when used Django form.. but in my case, I need to get this done using Ajax function only.
What could be the wrong in the view code ?
Here is my models.py
class td_Student(models.Model):
firstname = models.CharField(max_length=300,blank=True)
picpath=models.FileField(upload_to=unique_filename)
def unique_filename(instance, filename):
ext = filename.split('.')[-1]
filename = "%s_%s.%s" %(uuid.uuid4(),time.strftime("%Y%m%d_%H%M%S"), ext)
return os.path.join('documents/documents/'+time.strftime("%Y%m%d"), filename)
As per above logic, file name should be like 'documents/documents/20150716/a1f81a80-ce6f-446b-9b49-716b5c67a46e_20150716_222614.jpg' - This value should be stored in my database table.
settings.py
MEDIA_ROOT = 'C:/DJ/'
MEDIA_URL = '/DJ/'
The problem is not with Django, but with your AJAX post, you are just passing the name, hence Django receives and saves the name.
Solution: Once the user selects a file, change event will be emitteed, on this change evet you will have to grab the file instance using event.target.files store it in a local variable and pass it to picpath_Aj'.
// Add events
$('input[type=file]').on('change', prepareUpload);
// Grab the files and set them to our variable
function prepareUpload(event)
{
files = event.target.files;
}
Detailed guide is here http://abandon.ie/notebook/simple-file-uploads-using-jquery-ajax
And alternative Django solution with JS and backend code is https://github.com/skoczen/django-ajax-uploader
I have an API endpoint with Django Rest Framework to upload an image.
Can you spot what I'm doing incorrectly?
#models.py
class test(models.Model):
...
upload_path = 'upload/'
image = models.ImageField(upload_to=upload_path, null=True, blank=True)
...
#serializers.py
class TestSerializer(serializers.ModelSerializer):
image = serializers.ImageField(
max_length=None, use_url=True,
)
class Meta:
model = test
fields = ('id','name','image',...)
#views.py
#api_view(['GET', 'POST'])
def test_list(request, site_id, block_id):
....
if request.method == 'POST':
serializer = TestSerializer(data=request.DATA)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(
serializer.errors, status=status.HTTP_400_BAD_REQUEST)
else :
return Response(status=status.HTTP_403_FORBIDDEN)
#js
function setimage() {
var $input = $("#js_teaser_img");
var fd = new FormData;
fd.append('image', $input.prop('files')[0]);
$.ajax({
url: '/api/....',
data: fd,
processData: false,
contentType: false,
type: 'POST',
success: function (data) {
alert(data);
}
});
}
result image: ["No file was submitted."] 0: "No file was submitted."
result
Django REST Framework upload image: "The submitted data was not a file"
+
var reader = new FileReader();
reader.onload = function(e) {
var img_local = e.target.result;
$('.js_img_src').attr('src', img_local);
$.post('/api/..../7/', {'image':img_local} , function( data ) {
console.log(data);
});
}
reader.readAsDataURL(file);
From the client side in order to send files, you should use "multipart/form-data" (jQuery sets contentType as "application/x-www-form-urlencoded" instead by default).
Read this question on SO: Sending multipart/formdata with jQuery.ajax
Regarding instead python and django rest framework, you should use MultiPartParser and/or FileUploadParser in your API view and the preferred method for fle upload should be "put", as you can see in the reference here: http://www.django-rest-framework.org/api-guide/parsers/#fileuploadparser.
ps. if you use django rest framework, I strongly encourage you to use Angular instead of jQuery, since it offers an excellent integration for rest services... trust me is FAR BETTER! ;)