In Cloudinary Django SDK documentation for direct from browser uploading it gives example below for direct_upload_complete view.
#csrf_exempt
def direct_upload_complete(request):
form = PhotoDirectForm(request.POST)
if form.is_valid():
form.save()
ret = dict(photo_id = form.instance.id)
else:
ret = dict(errors = form.errors)
return HttpResponse(json.dumps(ret), content_type='application/json')
Immediately after this example it says Having stored the image ID, you can now display a directly uploaded image in the same way you would display any other Cloudinary hosted image: and gives template code example as follows:
{% load cloudinary %}
{% cloudinary photo.image format="jpg" width=120 height=80 crop="fill" %}
I am confused by this template code example because it does not relate at all to the Django view code which has response name ret. Screenshot of documentation is below.
What would I need to do to use Javascript to create a variable from the JSON object named ret and display it on the template page?
Below is Javascript I am using on the example's upload_prompt.html template. It works fine, returning a thumb of uploaded image and some Cloudinary data that show after image is uploaded (on cloudinarydone event).
But I also want to get the uploaded photo's Model id to create a link to the photo using the id.
Where in below Javascript can I get the photo_id key value from the JSON object named ret?
<script>
$(function () {
$('#direct_upload input[type="file"]')
.cloudinary_fileupload({
dropZone: '#direct_upload',
start: function (e) {
$('.status').text('Starting upload...');
},
progress: function (e, data) {
// $('.status').text('Uploading...');
$(".status").text("Uploading... " + Math.round((data.loaded * 100.0) / data.total) + "%");
},
fail: function (e, data) {
$(".status").text("Upload failed");
}
})
.on('cloudinarydone', function (e, data) {
$('.status').text('Updating backend...');
$.post(this.form.action, $(this.form).serialize()).always(function (result, status, jqxhr) {
$('.status').text(result.errors ? JSON.stringify(result.errors) : status);
});
var info = $('<div class="uploaded_info"/>');
$(info).append($('<div class="image"/>').append(
$.cloudinary.image(data.result.public_id, {
format: data.result.format,
width: 150,
height: 150,
crop: "fill"
})
);
$(info).append($('<div/>').append('image/upload/' + data.result.path));
$('.uploaded_info_holder').append(info);
});
});
</script>
Thanks to Brian Luk at Cloudinary Support, getting photo_id value is done in template's javascript POST request callback.
Maybe there are betters ways to do this, but I simply created variable photoid and then used that variable to create Photo model url.
$.post(this.form.action, $(this.form)
.serialize()).always(function (result, status, jqxhr) {
$('.status').text(result.errors ? JSON.stringify(result.errors) : status);
var photoid = JSON.stringify(result.photo_id);
$(info).append($('<div/>').append('link'));
Related
I'm using Flask with one of my wtforms TextAreaFields mapped to Trix-Editor. All works well except for images using the built toolbar attach button.
I'd like to save the images to a directory on the backend and have a link to it in the trix-editor text. I'm saving this to a database.
I can make this work by adding an <input type='file'/>in my template like so:
{{ form.description }}
<trix-editor input="description"></trix-editor>
<input type="file"/>
and the following javascript which I found somewhere as an example.
document.addEventListener('DOMContentLoaded', ()=> {
let contentEl = document.querySelector('[name="description"]');
let editorEl = document.querySelector('trix-editor');
document.querySelector('input[type=file]').addEventListener('change', ({ target })=> {
let reader = new FileReader();
reader.addEventListener('load', ()=> {
let image = document.createElement('img');
image.src = reader.result;
let tmp = document.createElement('div');
tmp.appendChild(image);
editorEl.editor.insertHTML(tmp.innerHTML);
target.value = '';
}, false);
reader.readAsDataURL(target.files[0]);
});
// document.querySelector('[role="dump"]').addEventListener('click', ()=> {
// document.querySelector('textarea').value = contentEl.value;
// });
});
This saves the image embedded in the text. I don't want that because large images will take up a lot of space in the database and slow down loading of the editor when I load this data back into it from the database.
It is also ugly having the extra button when Trix has an attachment button in it's toolbar. So, I'd like to be able to click the toolbar button and have it upload or if that is too hard, have the built in toolbar button save the image embedded.
To save the images to a folder instead of embedded, the Trix-editor website says to use this javascript https://trix-editor.org/js/attachments.js
In this javascript I have to provide a HOST so I use
var HOST = "http://localhost:5000/upload/"
and I set up a route in my flask file:
#tickets.post('/_upload/')
def upload():
path = current_app.config['UPLOAD_DIRECTORY']
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
session["id"] = filename
file.save(os.path.join(path, filename))
return send_from_directory(path, filename)
I can select an image and it shows in the editor and it uploads to the directory on my backend as expected. But when I save the form the location of the image is not in in the document text (should be in there as something like <img src="uploads/image.png>
On the python console I see
"POST /_upload/ HTTP/1.1" 404 -
I can make this go away if I change the return on my route to something like return "200" But all the examples I have seen about uploading files have this or a render_template. I don't want to render a template so I'm using this although I don't really understand what it does.
I'm assuming I need to return something the javasript can use to embed the image link in the document. But I'm a total newbie (like you didn't figure that out already) so I don't know what to do for the return statement (assuming this is where the problem lies).
If anyone else is trying to figure this out this is what I ended up doing.
Still needs a but of tweaking but works.
First I modified the example javascript for uploading to use Fetch instead of XMLHttpRequest
const editor = document.querySelector('trix-editor');
(function() {
HOST = '/_upload/'
addEventListener("trix-attachment-add", function(event) {
if (event.attachment.file) {
uploadFileAttachment(event.attachment)
}
// get rid of the progress bar as Fetch does not support progress yet
// this code originally used XMLHttpRequest instead of Fetch
event.attachment.setUploadProgress(100)
})
function uploadFileAttachment(attachment) {
uploadFile(attachment.file, setAttributes)
function setAttributes(attributes) {
attachment.setAttributes(attributes)
alert(attributes)
}
}
function uploadFile(file, successCallback) {
var key = createStorageKey(file)
var formData = createFormData(key, file)
fetch(HOST, {method: 'POST', body: formData}).then(function(response){
response.json().then(function(data){
alert(data.file, data.status)
if (data.status == 204) {
var attributes = {
url: HOST + key,
href: HOST + key + "?content-disposition=attachment"
}
console.log(attributes)
successCallback(attributes)
}
})
})
}
function createStorageKey(file) {
var date = new Date()
var day = date.toISOString().slice(0,10)
var name = date.getTime() + "-" + file.name
return [day, name ].join("/")
}
function createFormData(key, file) {
var data = new FormData()
data.append("key", key)
data.append("Content-Type", file.type)
data.append("file", file)
return data
}
})();
Then modified my Flask route (which I'll refactor, this was just slapped together to make it work):
def upload():
path = current_app.config['UPLOAD_DIRECTORY']
new_path = request.form["key"].split('/')[0]
file_upload_name = os.path.join(path, request.form["key"])
print(file_upload_name)
upload_path = os.path.join(path, new_path)
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename):
if not os.path.exists(upload_path):
os.mkdir(upload_path)
filename = secure_filename(file.filename)
session["id"] = filename
attachment = os.path.join(upload_path, filename)
file.save(attachment)
file.close()
os.rename(attachment, file_upload_name)
print(os.listdir(upload_path))
return jsonify({'file': attachment, 'status': 204})
return f'Nothing to see here'
Anyway, I hope that helps as it took me ages to figure out.
I am trying to upload image to an Etsy listing via their API v2. Their documentation have code example of image upload in PHP, in which the POST request body parameter is given like this
$params = array('#image' => '#'.$source_file.';type='.$mimetype);
How would I go about replicating this in JavaScript? I have tried sending the image blob as the image parameter but it does not seem to be working.
Edit: I am using npm oauth package. Here is my complete code that I am using to call the API.
var oauth1 = new OAuth1.OAuth(
'https://openapi.etsy.com/v2/oauth/request_token?scope=email_r%20listings_r%20listings_w%20listings_d',
'https://openapi.etsy.com/v2/oauth/access_token',
'api_key',
'api_secret',
'1.0A',
null,
'HMAC-SHA1'
);
oauth1.post(
'https://openapi.etsy.com/v2/listings/915677000/images',
req.user.etsy.oauth_token,
req.user.etsy.oauth_token_secret,
{
'listing_id': 915677000,
'image': <image has to go here>
},
'multipart/form-data',
function (e, data, response){
if (e) console.error(e);
// let body = JSON.parse(data);
console.log(data);
res.redirect('/create-listings')
});
I've added a working search field to a navigation bar that use jQuery autocomplete. The problem is it only works on the main page. On the other pages it tries to add the source url to the end of the page url.
For instance, on the home page it points to ajax_call/search, but on a company page (it's app that gathers internal data on companies) it points to company/847/ajax_call/search and obviously fails.
What do I need to do so the jQuery autocomplete functionality works across the entire site (without rewriting it in every template)?
Template
$(function() {
$("#tags").autocomplete({
minLength:2,
source: "ajax_call/search/",
select: function(event, ui){
window.location.href = ui.item.href;
}
});
});
URL
url(r'^ajax_call/search/$', views.ajax_company_search, name='search-field'),
View
def ajax_company_search(request):
if request.is_ajax():
query = request.GET.get('term', '')
company_names = Company.objects.filter(company_name__icontains=query).values('company_name', 'pk')
results = []
for name in company_names:
name_json = dict()
name_json['label'] = name['company_name']
name_json['value'] = name['company_name']
name_json['href'] = "company/" + str(name['pk']) + "/"
results.append(name_json)
data = json.dumps(results)
else:
data = 'fail'
mimetype = 'application/json'
return HttpResponse(data, mimetype)
You specified a relative URL in your JavaScript function. That's why it changes when you visit another page (with a different URL). In this case you want to use an absolute URL, like this: /ajax_call/search/.
Also, it is best practice to not hard-code your URLs in templates but to use reverse resolution:
$(function() {
$("#tags").autocomplete({
minLength:2,
source: {% url 'search-field' %},
select: function(event, ui){
window.location.href = ui.item.href;
}
});
});
I have a responsive-image-gallery- code in HTML5 that fetches an image from Filker. I want to fetch image from img folder and get the title automatically from the .jpg name this is the code
// fetch images from Flickr
function fetch_images() {
$.getJSON(api_flickr, {
tags: $("#searchinput").val(),
tagmode: "any",
format: "json"
})
.done(function(data) {
$.each(data.items, function(i, item) {
var url = item.media.m;
var title = item.title;
var elem = $("<div class='my show'><img src='"+url+"'/><div>"+
title+"</div>");
images.append(elem);
});
// add more-div and resize
images.append($("#morediv").clone().removeAttr("id"));
resize_images();
});
}
Flicker has an API that provides JavaScript with a JSON-formatted list of filenames.
In order for your script to do the same, you need to either provide it with the same type of API (maybe with a PHP script) or with a list of the filenames in the img folder.
References:
JSON - Learn what JSON is
PHP Script - Learn how to create a PHP script to output what you need
I want to load the PDF file dynamically and show on browser. PDF file is created on the fly when user clicks on button and the filename has timestamp in it. So i cannot give the PDF filename in the html code as shown below as it changes based on the timestamp(PDF file name is given along with the timestamp when it was created as shown in below spring controller).
Below is the code.
html code:
<div ng-controller="generatePDFController">
<button ng-click="generatePDF()">Re-Generate PDF</button>
<object data="C:/allFiles/PDFFiles/spreadDetails.pdf" type="application/pdf" width="100%" height="100%">
<iframe src="C:/allFiles/PDFFiles/spreadDetails.pdf" width="100%" height="100%" style="border: none;">
This browser does not support PDFs.
Download PDF
</iframe>
</object>
</div>
js code:
app.controller('generatePDFController', function($scope, MyService) {
$scope.generatePDF = function() {
MyService.createPDF().then(
function(response) {
$scope.pdf = response;
},
function(errResponse) {
});
}
});
//service call
_myService.createPDF = function() {
var deferred = $q.defer();
var repUrl = sURL + '/allDataGeneration/generatePDF.form';
$http.get(repUrl)
.then(
function(response) {
deferred.resolve(response.data);
},
function(errResponse) {});
return deferred.promise;
}
spring controller:
#RequestMapping(value = "/generatePDF", method = RequestMethod.GET)
public# ResponseBody List < MyDTO > generatePDF() {
List < MyDTO > response = service.getAllData();
//create PDF and write the response in it
createPDFFile(response);
return response;
}
void createPDFFile(List < MyDTO > res) {
String FILE_PATH = "C:\\allFiles\\PDFFiles\\spreadDetails";
String FILE_EXTENSION = "pdf";
DateFormat df = new SimpleDateFormat("MM-dd-yyyy hh-mm-ssa");
String filename = null;
try {
filename = FILE_PATH + df.format(new Date()) + "." + FILE_EXTENSION;
} catch (Exception e) {
e.printStackTrace();
}
File file = new File(filename);
System.out.println("-----filename------------ " + filename); //PDF file is created successfully
//spreadDetails07-13-2017 02-59-51PM ,when user clicks on GeneratePDF in UI, it hits this controller and generates the PDF
//logic to write the data inside PDF file
}
The above shown code is the complete flow of my sample application. Now when user clicks on Re-Generate PDF button, it comes to above mentioned spring controller creates a file with timestamp and writes the data in it.How to pass the newly created pdf filename to the html code <object data="C:/allFiles/PDFFiles/spreadDetails.pdf" .. so that when pdf file is created it dynamically loads and show on UI.
---EDITED---
Please see the above edited code. createPDF(List<MyDTO>) is a new method in which i'm creating a pdf file and writing the content. I will be reusing this method.
Try to follow these steps :
Change the signature of the Java method generatePDF() in order to return a String representing the name of your file. This gives you the possibility to pass the name of the file to your JavaScript ;
In your controller, do $scope.pdfName = response. This way the name of the file is store the variable $scope.pdfName ;
Last step, replace <object data="C:/allFiles/PDFFiles/spreadDetails.pdf" ...> by <object data="{$scope.pdfName}" ...>
This should work.
Marine
EDIT given your own edit :
Your method generatePdf() is incorrect : you wrote that it must return a List<MyDto> but the keyword return is nowhere.
Do you really need to return he object List<MyDto> ? In any case, you need to return the name of the file to be able to use it in your JavaScript. So, you have two solutions : either this method only returns a String representing the name of the PDF, or it returns an object with two fields, one String and one List<MyDto>. In this second cas, you will need to do
$scope.pdfName = response.fieldContainingTheNameOfTheFile.