microsoftgraph / msgraph-sdk-javascript

Microsoft Graph client library for JavaScript
https://graph.microsoft.com
MIT License
754 stars 229 forks source link

How can I put a profile photo on the html page? #214

Closed narse closed 5 years ago

narse commented 5 years ago

Feature Request

How can I put a profile photo on the html page?

https://docs.microsoft.com/en-us/graph/api/profilephoto-get?view=graph-rest-1.0

how a profile picture will appear even if I read the following document. would you make me a JavaScript sample code?

muthurathinam commented 5 years ago

Set up your environment as explained in the readme.

Note: The explanation in the above readme is for our latest preview which is more light weight and has more advantages over the stable version. 2.0.0-Preview.4 is the latest tag you might want to consider for installation

async function updateProfilePicture () => {
    try {
        let response = await client.api("/me/photo/$value").get();
               // Assuming that profileImg is the image tag in which you want to render your profile image
        document.getElementById("profileImg").setAttribute("src", URL.createObjectURL(response));
    } catch (error) {
        console.error(error);
    }
}
narse commented 5 years ago

Thank you @muthurathinam I succeeded in showing the profile image using the code you wrote me.

Best Regards

oshihirii commented 1 year ago

I get this error:

common.js:89 Uncaught TypeError: Failed to execute 'createObjectURL' on 'URL': Overload resolution failed.

When I use this:

var blobUrl = URL.createObjectURL(results.data);
$("#main_content").html(`<img src="${blobUrl}">`); 
sebastienlevert commented 1 year ago

You will need to request the RAW response and get a Base64 version of the blob it returns. We are using a similar approach in the Graph Toolkit : https://github.com/microsoftgraph/microsoft-graph-toolkit/blob/31c8e8a21ec68521e31cc1e78e09c57f994b5c3b/packages/mgt-components/src/graph/graph.photos.ts#L50

oshihirii commented 1 year ago

How do you request the RAW response?

It is not mentioned in the docs:

https://learn.microsoft.com/en-us/graph/api/profilephoto-get

It just says that this:

GET https://graph.microsoft.com/v1.0/me/photo/$value

returns:

the binary data of the requested photo

There is a section at the bottom of the page with instructions on how to use the returned data to display an image:

https://learn.microsoft.com/en-us/graph/api/profilephoto-get?view=graph-rest-1.0#using-the-binary-data-of-the-requested-photo

const url = window.URL || window.webkitURL;
const blobUrl = url.createObjectURL(image.data);
document.getElementById(imageElement).setAttribute("src", blobUrl);

but that approach doesn't work for me.

For reference, I have tried to use the following approaches to defining the src value of an <img> element.

All approaches return some form of undesirable behaviour:

btoa(results.data)

produces:

DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.

btoa(encodeURIComponent(results.data))

produces a broken image icon.

URL.createObjectURL(results.data)

produces:

Uncaught TypeError: Failed to execute 'createObjectURL' on 'URL': Overload resolution failed.

And using just the binary data fills the page with 'diamond' and random characters.

sebastienlevert commented 1 year ago

Using the latest version of our SDK this works:

const image = await client.api('/me/photo/$value').get();
const url = window.URL || window.webkitURL;
const blobUrl = url.createObjectURL(image);
document.getElementById('avatar').setAttribute("src", blobUrl);
oshihirii commented 1 year ago

That is not working for me.

Below are three attempts.

They show the backend code, the frontend code and the errors they produce.

My environment is:

Obviously, I am making a request from backend code, so my application is a 'confidential client' that uses the auth code flow.

Attempt 01 - returning response from Graph API request

// BACKEND - node.js/express application   
const options = {
    headers: {
        Authorization: `Bearer ${IDPaccessToken}`
    }
};

const response = await axios.get("https://graph.microsoft.com/v1.0/me/photo/$value", options);
res.json({ data: response}); // <---- returning 'response' to the frontend  

// FRONTEND - send request to node.js 
const ajax_api_graph_get = () => {

    $.ajax({
        url: "/graph",
        data: {},
        dataType: 'json',
        cache: false,
        headers: {},
        success: function(results) {     

            const url = window.URL || window.webkitURL;
            const blobUrl = url.createObjectURL(results.data);        

            $('#main_content').html(`<img src='${blobUrl}'>`);

        },
        statusCode: {
            500: function() {
                console.log("well, that didn't work");
            }
        }
    });

}

// click handler
$(document).on("click", "#get_my_photo", function(e) {
    e.preventDefault();  
    ajax_api_graph_get(); 
}); 

Produces error on backend:

TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'ClientRequest'
    |     property 'socket' -> object with constructor 'TLSSocket'
    --- property '_httpMessage' closes the circle
    at JSON.stringify (<anonymous>)
    at stringify (C:\Users\ME\Documents\REPOS\MY_REPO\node_modules\express\lib\response.js:1150:12)
...

Attempt 02 - returning response.data from Graph API request

// BACKEND - node.js/express application   
const options = {
    headers: {
        Authorization: `Bearer ${IDPaccessToken}`
    }
};

const response = await axios.get("https://graph.microsoft.com/v1.0/me/photo/$value", options);
res.json({ data: response.data});  // <---- returning 'response.data' to the frontend  

// FRONTEND - send request to node.js 
const ajax_api_graph_get = () => {

    $.ajax({
        url: "/graph",
        data: {},
        dataType: 'json',
        cache: false,
        headers: {},
        success: function(results) {     

            const url = window.URL || window.webkitURL;
            const blobUrl = url.createObjectURL(results.data);        

            $('#main_content').html(`<img src='${blobUrl}'>`);

        },
        statusCode: {
            500: function() {
                console.log("well, that didn't work");
            }
        }
    });

}

// click handler
$(document).on("click", "#get_my_photo", function(e) {
    e.preventDefault();  
    ajax_api_graph_get(); 
}); 

Produces error on FRONTEND:

Uncaught TypeError: Failed to execute 'createObjectURL' on 'URL': Overload resolution failed.

Attempt 03 - returning response.data from Graph API request (using a buffer)

Out of desperation, I also tried this:

// BACKEND - node.js/express application  
const options = {
    headers: {
        Authorization: `Bearer ${IDPaccessToken}`,
        responseType: "arraybuffer"
    }
};

const response = await axios.get("https://graph.microsoft.com/v1.0/me/photo/$value", options);
const avatar = Buffer.from(response.data, "binary").toString("base64");
res.json({ data: avatar});

// FRONTEND - send request to node.js 
const ajax_api_graph_get = () => {

    $.ajax({
        url: "/graph",
        data: {},
        dataType: 'json',
        cache: false,
        headers: {},
        success: function(results) {     

            $('#main_content').append(`<img src='data:image/jpeg;base64,${results.data}'>`);

        },
        statusCode: {
            500: function() {
                console.log("well, that didn't work");
            }
        }
    });

}

// click handler
$(document).on("click", "#get_my_photo", function(e) {
    e.preventDefault();  
    ajax_api_graph_get(); 
}); 

Produces a broken image icon on the frontend.

EDIT:

I also tried adding this to the jQuery ajax request:

contentType: "image/jpeg",

And also this in the axios request headers in node.js

headers: {
    Authorization: `Bearer ${IDPaccessToken}`,
    responseType: 'blob'
}

None of these attempts worked either.

mrcrittall2016 commented 1 year ago

Hi @oshihirii I had exactly the same issues as you.

In agreement with @sebastienlevert, using the msgraph sdk (https://github.com/microsoftgraph/msgraph-sdk-javascript#installation) saved a lot of pain and this photo endpoint just worked and took care of ensuring the correct Blob output which you can happily put into the url.createObjectURL function as is.

Hope you got it working in the end!

riebecj commented 6 months ago

I just wanted to post here, it's quite possible to render the image without the client. I use:

export async function getPhoto(instance: PublicClientApplication) : Promise<string> {
    const options = await getOptions(instance)

    return fetch(graphConfig.photoEndpoint, options)
        .then(response => response.blob())
        .then((blob) => {return URL.createObjectURL(blob)})
        .catch(error => {console.log(error); return ""});
}

Then, in the Avatar component, I do:

<Avatar src={props.photo} alt={props.graphData.displayName} />

Where props.photo is a reference to the state containing the return from URL.createObjectURL(blob).