How to display user's image in Microsoft Graph using python - python

I tried using
GET https://graph.microsoft.com/v1.0/me/photo/$value to get the user's image/photo but it only returns an HTTP 200 status code. How can I get the binary data?
I've also tried using the content.property as suggested in a similar post but get a .format is not an attribute of the dict.
#app.route("/photo")
def get_photo():
token = _get_token_from_cache(app_config.SCOPE)
if not token:
return redirect(url_for("login"))
photo = requests.get(app_config.PHOTO_ENDPOINT,
headers={'Authorization': 'Bearer ' + token['access_token']})
print(photo.status_code)
return photo

Gets a profile photo, and optionally saves a local copy. Returns a tuple of the raw photo data, HTTP status code, content type, and saved filename. Refer to this sample.
def profile_photo(session, *, user_id='me', save_as=None):
"""Get profile photo, and optionally save a local copy.
session = requests.Session() instance with Graph access token
user_id = Graph id value for the user, or 'me' (default) for current user
save_as = optional filename to save the photo locally. Should not include an
extension - the extension is determined by photo's content type.
Returns a tuple of the photo (raw data), HTTP status code, content type, saved filename.
"""
endpoint = 'me/photo/$value' if user_id == 'me' else f'users/{user_id}/$value'
photo_response = session.get(api_endpoint(endpoint),
stream=True)
photo_status_code = photo_response.status_code
if photo_response.ok:
photo = photo_response.raw.read()
# note we remove /$value from endpoint to get metadata endpoint
metadata_response = session.get(api_endpoint(endpoint[:-7]))
content_type = metadata_response.json().get('#odata.mediaContentType', '')
else:
photo = ''
content_type = ''
if photo and save_as:
extension = content_type.split('/')[1]
filename = save_as + '.' + extension
with open(filename, 'wb') as fhandle:
fhandle.write(photo)
else:
filename = ''
return (photo, photo_status_code, content_type, filename)

Alternate approach, based on the original question's code, if you want to display the resulting image on a web page.
from base64 import b64encode
#app.route("/photo")
def get_photo():
token = _get_token_from_cache(app_config.SCOPE)
if not token:
return redirect(url_for("login"))
response = requests.get(
app_config.PHOTO_ENDPOINT,
headers={'Authorization': 'Bearer ' + token['access_token']}
)
content_type = response.raw.getheader('Content-Type')
return render_template('index.html',
photo_data=b64encode(response.content),
photo_content_type=content_type)
Then in the index.html template you can display the photo like so:
<html>
<body>
<img src="data:{{ photo_content_type }};base64,{{ photo_data }}" />
</body>
</html>

Call Api: -
Axios.get('https://graph.microsoft.com/v1.0/me/photo/$value', {
headers: { 'Authorization': 'Bearer '+AccessToken },
responseType: 'blob'
}).then(o => {
const url = window.URL || window.webkitURL;
const blobUrl = url.createObjectURL(o.data);
self.setState({ imageUrl: blobUrl });
})
JSX: -
<img alt="image" src={this.state.imageUrl} />

Here is what worked for me:
from base64 import b64encode
token = _get_token_from_cache(app_config.graphSCOPE)
aadPhotoURI = "https://graph.microsoft.com/v1.0/me/photo/$value"
response = requests.get(aadPhotoURI, headers={'Authorization': 'Bearer ' +
token['access_token']},)
content_type = response.raw.getheader('Content-Type')
return render_template(
'usersettings.html',
photo_data = b64encode(response.content).decode(),
photo_content_type = content_type)
then in the .html page added:
<img class="account-img" alt="" src="data:{{ photo_content_type }};base64,{{ photo_data }}"/>
CSS for account-img:
.account-img {
width: 40px;
float: right;
border-radius: 50%;
}

Related

Generate dynamic div containers on submit in Flask app

I am new in Flask. My goal is to generate dynamic div containers every time I upload a dxf file and next submit happens. For example: one file uploaded- one div shown; two files uploaded- two divs shown and so on.
I can convert uploaded dxf files to .png images and I would like to show these images in div elements displayed after every upload.
I use input tag to upload files (type='file') and Java Script to generate dynamic elements (divs and their child tags).
The problem is that every time I upload file, the template is loading again and no new content is shown except the image of the last uploaded dxf. Please, give me a piece of advice to solve it.
HTML
...
<form enctype="multipart/form-data" id="uploadForm" action="/upload_files" name="uploadForm" method="post">
DXF file: <input type="file" id="dxfUpload" onchange="form.submit(); createConfigure();" name="dxfUpload" />
<div id="calcHolder" name="calcHolder">
<script type="text/javascript">
function createConfigure() {
var div = document.createElement("div");
div.id = "dxf-"+Math.random() * 100000000000000000 + "-"
+ window.performance.now() * 100000000000000000;
id_div=div.id;
div.className = 'border pad';
div.style.width = "640px";
div.style.height = "200px";
document.getElementById("calcHolder").appendChild(div);
var img = document.createElement("img");
img.setAttribute("src", "{{url_for('static', filename=dxfName+'.png')}}");
img.setAttribute("alt", "no image");
img.setAttribute("height", "120px");
img.setAttribute("width", "120px");
document.getElementById(id_div).appendChild(img);
var array = ["Carbon Steel","Stainless Steel","Aluminium"];
var selectMaterial = document.createElement("select");
document.getElementById(id_div).appendChild(selectMaterial);
for (var i = 0; i < array.length; i++) {
var option = document.createElement("option");
option.value = array[i];
option.text = array[i];
selectMaterial.appendChild(option);
}
var selectThickness = document.createElement("select");
document.getElementById(id_div).appendChild(selectThickness);
for (i = 1; i <= 16; i++) {
var opt = document.createElement('option');
//opt.value = i;
opt.innerHTML = i + ' mm';
selectThickness.appendChild(opt);
}
var quantity = document.createElement("input")
quantity.type="number";
quantity.value="1";
quantity.name="quantity";
quantity.min="1";
quantity.max="50";
quantity.onkeyup= function maxReach(){if(quantity.value > 50) quantity.value=50;};
document.getElementById(id_div).appendChild(quantity);
var btn = document.createElement("button");
btn.innerHTML = "Delete";
btn.type = "button";
document.getElementById(id_div).appendChild(btn);
btn.onclick = function() {div.remove();};
}
</script>
{{ html | safe }}
</div>
</form>
...
Python
#app.route('/upload_files', methods=['POST'])
def upload_files():
try:
if request.method == 'POST':
dxf_file = request.files['dxfUpload']
full_filename = os.path.join(app.config['UPLOAD_FOLDER'],dxf_file.filename)
dxf_file.save(full_filename)
first = DXF2IMG()
first.convert_dxf2img([full_filename],img_format='.png')
html="<img src="+url_for('static', filename=dxf_file.filename+'.png' )+" width='120' height='120' />"
return render_template('upload_files.html',dxfName=dxf_file.filename, html=html)
except:
...
#something happens
The result now
Desired result
Once the form.submit() function is executed, the form will be sent as a regular post request. For this reason, the following function is no longer executed and the entire page is reloaded.
In order to submit the form and change the content of the existing page, it is necessary to use AJAX.
This example shows you how to submit the form to the server and receive a JSON response containing the URLs of the received file and the generated image.
As soon as the submit button is pressed, the form data is packed into a FormData object and sent via AJAX using the fetch function. The browser's default behavior for a submit event is suppressed and the form is reset. The received file is processed by the server and the associated URLs are sent back to the client in JSON format. Now the document can be changed with the received data.
Remember this is just a minimal example to help you achieve your goals and implement your concept.
Flask (app.py)
import os
import ezdxf
from ezdxf.addons.drawing import matplotlib
from flask import Flask
from flask import (
jsonify,
make_response,
render_template,
url_for
)
from werkzeug.utils import secure_filename
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
def dxf2png(source, target):
doc = ezdxf.readfile(source)
msp = doc.modelspace()
auditor = doc.audit()
if auditor.has_errors:
raise Exception('Conversion failed.')
matplotlib.qsave(doc.modelspace(), target)
#app.route('/upload', methods=['POST'])
def upload():
if 'dxf-file' in request.files:
file = request.files['dxf-file']
if file.filename != '':
filename = secure_filename(file.filename)
filepath = os.path.join(app.static_folder, filename)
destname, _ = os.path.splitext(filename)
destname = f'{destname}.png'
destpath = os.path.join(app.static_folder, destname)
file.save(filepath)
try:
dxf2png(filepath, destpath)
except:
os.remove(filepath)
return make_response('', 400)
return make_response(
jsonify(
target=url_for('static', filename=filename),
preview=url_for('static', filename=destname)
),
200
)
return make_response('', 400)
HTML (templates/index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Index</title>
<style media="screen">
.preview {
width: 120px;
height: auto;
}
</style>
</head>
<body>
<form name="dxf-upload" method="post" enctype="multipart/form-data">
<input type="file" name="dxf-file" />
<input type="submit" />
</form>
<div id="dxf-files"></div>
<script type="text/javascript">
((uri) => {
function createPreview(target, preview) {
const divElem = document.createElement('div');
divElem.innerHTML = `<img src="${preview}" class="preview" />`;
const outElem = document.getElementById('dxf-files');
outElem.append(divElem);
}
const form = document.querySelector('form[name="dxf-upload"]');
form.addEventListener('submit', evt => {
evt.preventDefault();
const formData = new FormData(evt.target);
fetch(uri, {
method: 'POST',
body: formData
}).then(resp => resp.json())
.then(data => {
const { target, preview } = data;
createPreview(target, preview);
});
evt.target.reset();
});
})({{ url_for('.upload') | tojson }});
</script>
</body>
</html>

How to get path of all uploaded files in Flask

I am making a rest API for multipart request API code is working fine but I am unable to get URL of multiple files any body know how to get this done code I done so far:
#app.route('/multiple-files-upload', methods=['POST'])
def upload_file():
randNum = random.randint(10, 900)
appendRand = "SWM-"+str(randNum)
date = str(datetime.date(datetime.now()))
# subject = request.form['subject']
# details = request.form['details']
# province = request.form['province']
# district = request.form['district']
# tehsil = request.form['tehsil']
# lat = request.form['lat']
# lon = request.form['lon']
# username = request.form['username']
# status = request.form['status']
files = request.files.getlist('files')
errors = {}
data = {}
success = False
for file in files:
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
appendDate = str(appendRand)+"-"+date+"-"+filename
file.save(os.path.join(app.config['UPLOAD_FOLDER'],appendDate))
success = True
else:
errors[file.filename] = 'File type is not allowed'
# if success and errors:
# errors['message'] = 'File(s) successfully uploaded'
# resp = jsonify(errors)
# resp.status_code = 500
# return resp
if success:
filename = secure_filename(file.filename)
url = appendPath +'/'+appendDate
data = {"URL":url}
resp = jsonify({"Error":"flase","Code":"00",'Message': 'Files successfully uploaded',"Data":data})
resp.status_code = 200
return resp
else:
resp = jsonify(errors)
resp.status_code = 500
return resp
Response I get:
{
"Error": "flase",
"Code": "00",
"Message": "Files successfully uploaded",
"Data": {
"URL": "/uploads/SWM-882-2022-02-17-API.PNG"
}
}
Desired response:
{
"Error": "flase",
"Code": "00",
"Message": "Files successfully uploaded",
"Data": {
"URL": "/uploads/SWM-882-2022-02-17-API.PNG"
"URL": "/uploads/SWM-882-2022-02-17-API.PNG"
"URL": "/uploads/SWM-882-2022-02-17-API.PNG"
"URL": "/uploads/SWM-882-2022-02-17-API.PNG"
}
}
E.g. when I upload multiple files I get multiple URLs now only get 1 URL
The example below follows your code and shows how to upload multiple files to the server using AJAX. As I am assuming you are trying.
Depending on whether an error occurs or not, a different response from the server is returned.
This contains a message and possible error messages, each of which is assigned to a file name. Furthermore, the resulting urls of the successfully uploaded files are listed.
Flask (app.py)
import os
from flask import Flask
from flask import (
jsonify,
render_template,
request,
url_for
)
from datetime import date
from random import randint
from werkzeug.utils import secure_filename
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = app.static_folder
#app.route('/')
def index():
return render_template('index.html')
def allowed_file(filename):
# Your validation code here!
return True
#app.route('/upload', methods=['POST'])
def upload():
errors = {}
data = []
prefix = f'SWM-{randint(10,900)}-{date.today()}'
files = request.files.getlist('files')
for file in files:
if file.filename != '' and allowed_file(file.filename):
filename = secure_filename(file.filename)
filename = f'{prefix}-{filename}'
file.save(os.path.join(
app.config['UPLOAD_FOLDER'],
filename
))
data.append({ 'url': filename })
else:
errors[file.filename] = 'File type is not allowed'
return jsonify({
'message': 'An error has occurred.' if errors else 'Files successfully uploaded',
'errors': errors,
'data': data
}), 400 if errors else 200
HTML (templates/index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Index</title>
</head>
<body>
<form name="my-form" method="post">
<input type="file" name="files" multiple />
<input type="submit" />
</form>
<script type="text/javascript">
((uri) => {
const formElem = document.querySelector('form[name="my-form"]');
formElem.addEventListener('submit', evt => {
evt.preventDefault();
fetch(uri, {
method: 'post',
body: new FormData(evt.target)
}).then(resp => resp.json())
.then(data => {
console.log(data);
});
evt.target.reset(),
});
})({{ url_for('upload') | tojson }});
</script>
</body>
</html>

How to force an instant image refresh from an Angular Safeurl without reloading the page

I've created an angular component (product) on which a user is able to change an image (product's image).
html:
<img [attr.id]="_product.id" [attr.src]="_product.thumbnail_url"/>
When the image is chosen by the user from a custom file picker component on a modal form. The following method is called to reload the image in the parent product component.
The imageBinary here is a base64 blob binary image returned from a python FLASK REST API endpoint which is connected to a mysql database.
reloadProductImage(new_image_id):void{
let newUrl:SafeUrl;
let urlStr:string;
this.productservice.getImage(new_image_id).subscribe((filedata) =>{
let imageBinary = filedata.thumbnail;
urlStr = 'data:image/jpeg;base64,' + imageBinary + '?'+Date.now();
newUrl = this.domSanitizer.bypassSecurityTrustUrl(urlStr);
this.form.patchValue({image_id: new_image_id});
this._product.thumbnail_url = newUrl;
this._product.image_id = new_image_id;
I've found many examples how to force the browser to a reload on a regular src url by appending a timestamp to the url. However, these examples do not seem to apply to a blob stored in a saferurl.
inspect image element
<img _ngcontent-snv-c187="" id="146914e3-6e0a-409f-8046-ff04580f6242" src="data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgA+++very+long+blob+++vBzu3ZFwwAAAABJRU5ErkJggg==?1619156455998">
Console Error message
Failed to load resource: net::ERR_INVALID_URL
Failed attempts
I've tried:
newUrl = 'data:image/jpeg;base64,' + imageBinary + '?'+ Date.now();
and
newUrl = 'data:image/jpeg;base64,' + imageBinary + '?datetime='+ Date.now();
Background info
For illustration purposes, I'll post my Angular and Python code here (methods in a Flask SQLAlchemy DB Model) which post an image file to a Flask REST API, generate the thumbnail from the base64 image and return it to Angular. Note: the get function is called in a GET request. The Create function is called in a POST request.
Angular FileUpload component:
import { HttpClient } from '#angular/common/http';
import { Component, OnInit, Output, EventEmitter } from '#angular/core';
import {NotificationService} from '../..//notification/notification.service';
#Component({
selector: 'fileupload-form',
template: `
<p>
Productfoto toevoegen
</p>
<input type="file" (change)="onFileSelected($event)">
<button type="button" (click)="onUpload()">Upload</button>
`,
styles: [
]
})
export class FileuploadComponent implements OnInit {
selectedFile: File = null;
#Output()
fileSubmitted = new EventEmitter();
constructor(private http: HttpClient,
private notificationService: NotificationService,
) { }
onFileSelected(event){
this.selectedFile = <File>event.target.files[0];
}
onUpload(){
const fd = new FormData();
fd.append('inputFile', this.selectedFile , this.selectedFile.name);
this.http.post(['/offerte/api/files','upload'].join('/'),fd)
.subscribe(res => {
console.log(res);
this.notificationService.success(`Bestand ${this.selectedFile.name} is opgeslagen.`);
this.fileSubmitted.emit(res);
})
}
ngOnInit(): void {
}
}
Python Flask Endpoints:
Imports
import logging
from io import BytesIO
from flask import request, send_file
from flask_restx import Resource
from api.serializers import fileobject, page_of_files
from api.parsers import pagination_arguments
from api.restplus import api
from database.models import File
from flask_jwt_extended import jwt_required, get_jwt_identity
logger = logging.getLogger()
ns_files = api.namespace('files', description='Operations related to file management')
POST
#ns_files.route('/upload')
class Upload(Resource):
#api.doc(security='jwt')
#jwt_required
def post(self):
'''Upload a new file, returns id of new file'''
inputFile = request.files['inputFile']
logger.info('create new file %s', request.json)
current_user = get_jwt_identity()
return File.create(inputFile, current_user)
GET
#ns_files.route('/<string:file_id>')
#api.response(404, 'file not found')
class Details(Resource):
#api.marshal_with(fileobject)
def get(self, file_id):
'''Download a new file from the db (binary storage)'''
logger.info('loading file %s')
current_user = get_jwt_identity()
returnedFile = File.get(file_id)
return returnedFile
Python FLASK REST API functions
from PIL import Image
import magic
from base64 import b64encode
import io
accepted_mime_types = {'jpe':'image/jpeg','jpeg':'image/pjpeg','png':'image/png'}
def get(id):
return Product.query.filter(Product.id == id).one()
def create(inputFile, current_user):
filename = inputFile.filename
file_obj = File(
filename = filename,
extension = '.jpg',
folderpath = 'folderpath',
databinary = inputFile.read(),
category = 'default',
description = 'beschrijving...',
original_filename = filename,
created_by = current_user,
modified_by= current_user)
MAX_SIZE = (150, 150)
thumbnailFile, mimetype = File.getThumbnail(inputFile, MAX_SIZE)
if not thumbnailFile is None:
output = io.BytesIO()
thumbnailFile.save(output, format='PNG')
hex_data = output.getvalue()
file_obj.thumbnail = hex_data
else:
raise NameError(mimetype)
db.session.add(file_obj)
db.session.commit()
return str(file_obj.id)
def mimetype(file_obj):
return magic.from_buffer(file_obj.read(2048), mime=True)
def getThumbnail(file_obj, size):
file_obj.seek(0)
real_mime_type = magic.from_buffer(file_obj.read(2048), mime=True)
if (real_mime_type in accepted_mime_types.values()):
thumbnailImage = Image.open(file_obj)
thumbnailImage.thumbnail(size)
return thumbnailImage, real_mime_type
else:
raise NameError(real_mime_type)

Auto download not working for Django FileResponse

I need to let the Django auto download the generated file.
Tried all different solutions online, none of them works.
Views.py
def validate(request):
if request.method == 'POST':
filename = request.POST.get('source_file')
file_path = os.path.join(settings.MEDIA_ROOT, 'SourceFiles', filename)
region = request.POST.get('region')
product_type = request.POST.get('product_type')
result = validateSource.delay(file_path, region, product_type)
output_filepath, log_filepath = result.get()
if os.path.exists(output_filepath) and os.path.exists(log_filepath):
zip_filename = zipFiles([output_filepath, log_filepath], filename)
zip_filepath = os.path.join(settings.MEDIA_ROOT, zip_filename)
response = FileResponse(open(zip_filepath, 'rb'), as_attachment=True)
return response
raise Http404
Template: code for the form POST.
$(document).on('submit', '#productForm', function(e){
e.preventDefault();
var inputFilePath = document.getElementById('sourceFileInput').files.item(0).name;
$.ajax({
method: 'POST',
url: 'validate/',
data: {
source_file: inputFilePath,
region: $("#Region-choice").val(),
product_type: $("#Product-type").val()}
})
.done(function(){
document.getElementById('lblStatus').innerHTML = "Result: <br/>"
document.getElementById('lblStatusContent').innerHTML = "Success!"
})
.fail(function(req, textStatus, errorThrown) {
document.getElementById('lblStatus').innerHTML = "Result: <br/>"
alert("Something went wrong!:" + textStatus + ' ' + errorThrown )
});
});
});
It's not possible to download files to your computer via an ajax (XHR) request. So you need to redirect the user actually (setting window.location) to a view that downloads the file. Or you can add as a result of the successful POST a button the current page so the user can download the file. In any case, you need to move the file download to a different view so a standard GET request can fetch it.
But your code to return the file in Django (using FileResponse) is correct.
There's also an explanation with an alternative way of doing it here
def validate(request):
if request.method == 'POST':
filename = request.POST.get('source_file')
file_path = os.path.join(settings.MEDIA_ROOT, 'SourceFiles', filename)
region = request.POST.get('region')
product_type = request.POST.get('product_type')
result = validateSource.delay(file_path, region, product_type)
output_filepath, log_filepath = result.get()
if os.path.exists(output_filepath) and os.path.exists(log_filepath):
zip_filename = zipFiles([output_filepath, log_filepath], filename)
zip_filepath = os.path.join(settings.MEDIA_ROOT, zip_filename)
with open(zip_filepath, 'rb') as fh:
response = HttpResponse(fh.read(), content_type="application/force-download")
response['Content-Disposition'] = 'attachment; filename=' + os.path.basename(zip_filepath)
return response
raise Http404

Ajax POST to flask to download a binary (cytoscape in use)

I'm using cytoscape.js to create a menu where I can download files quickly. I'm using an Ajax POST in order to pass the file name back to flask in order to download. For some reason, I can get all the information back but for whatever reason I cannot get the file to download. I've tried two methods so far.
AJAX Post:
{{
content: 'Download',
select: function(ele) {{
//this is to get the name of URI
var loc = window.location.pathname
var postData = {{
"element": ele.id(),
"source": loc
}}
$.ajax({{
url: '/get_file',
type: "POST",
contentType: 'application/json',
data: JSON.stringify(postData),
dataType: 'json',
success: function(response) {{
console.log("got it!")
}},
error: function(xhr) {{
console.log("Nope!")
}}
}})
Now for the flask back end, we have method one, and the commented section shows method 2 & 3 (in the if loop (if os.path.isfile(SAVE_PATH)):
#app.route('/get_file', methods=['POST'])
def get_file():
print("This is request data: {}".format(request.data))
requests = request.get_json()
element = requests['element']
source = requests['source']
#this is where loc is retrieved from ajax post, in format of /static/{filename}.html
for change in ['/static/', '.html']:
if change in source:
source = source.replace(change,"")
print("source: {}".format(source))
SAVE_PATH = os.path.curdir + "/results/" + source + "/" + element
SAVE_DIRECTORY = os.path.curdir + "/results/" + source + "/"
if os.path.isfile(SAVE_PATH):
downloaded_file = open("{}".format(SAVE_PATH), 'rb').read()
#res = send_from_directory(SAVE_PATH.replace("./", ""), element, attachment_filename=element, mimetype="application/octet-stream", as_attachment=True)
#res = send_file(SAVE_PATH, as_attachment=True, attachment_filename=element, mimetype='application/octet-stream')
#return res
return Response(
downloaded_file,
mimetype="application/octet-stream",
headers={"Content-disposition":
"attachment; filename={}".format(element)})
else:
print("failed")
return "failed"
Now I'm getting all the correct response, when I print out downloaded_file, I get the binary output, but for whatever reason, it just isn't downloading.
Looks like you could construct the download path on the frontend and request the file directly, something like this:
location = loc.split("/");
path = "results/" + location[location.length - 1].split(".")[0] + "/" + ele.id();
window.location.href = path;

Categories

Resources