Reputation: 493
I am displaying profile picture of a user on a vue/nuxt app. This picture is uploaded to S3 through a file API. While receiving the image from file API and displaying the profile pic, I am not able to convert it to the right format.
My problems:
const blob = new Blob([response.data],{type=response.type}
blob
Step 1: File API - Reading from S3 using AWS client v3 (File: FileService.js)
let goc = new GetObjectCommand({
...
});
s3client.send(goc)
.then(response => {
if(response){
const body = response.Body;
const tempFileName = path.join(config.FILE_DOWNLOAD_PATH, file_name);
const tempFile = fs.createWriteStream(tempFileName);
body.pipe(tempFile);
resolve(Service.createSuccessResponse(
{
file_name_local: tempFileName,
file_name: file_name,
content_length: response.ContentLength,
}
,"SUCCESS")); // This is **responsePayload** in the next snippet.
...
Step 2: Send the response using expressjs (File: Controller.js)
if(responsePayload.file_name_local){
response.set('Content-Length',responsePayload.content_length);
response.write(fs.readFileSync(responsePayload.file_name_local));
response.end();
response.connection.end();
}
Step 3: Define image (File: view-profile.vue)
<template>
...
<v-img src="dpURL"/>
...
</template>
Step 4 - EAGER DOWNLOAD: Receive image as blob (File: view-profile.vue)
<script>
...
mounted(){
...
this.getDP(this.profileInfo.dp_url).then(r => {
this.dpURL = window.URL.createObjectURL(new Blob([r.data])); // Do we need explicit MIME here?
}).catch(e => {
console.error("DP not retrieved: "+JSON.stringify(e));
})
...
},
methods: {
...
getDP(file_name){
return new Promise(
async(resolve, reject) => {
callGenericAPI({
url: this.$config.API_HOST_URL+'/file',
configObj: {
method: 'get',
params: {
file_name: file_name,
file_category: 'user-dp'
},
headers: {
'api_token': idToken,
'Cache-Control':'no-cache'
},
responseType: 'blob'
},
$axios: this.$axios //Just some crazy way of calling axios. Don't judge me here.
})
.then(r => {
console.log("DP received.");
resolve(r.data);
})
.catch(e => {
console.error("DP not received." + JSON.stringify(e));
reject(e);
})
}
)
},
...
}
Step 5 - LAZY DOWNLOAD: Receive image as blob (File: view-profile.vue > custom-link.vue - child) after clicking the link
File: view-profile.vue
<template>
...
<CustomLink file_name="fileName" file_category="USER_DP" label="User Profile Picture"/>
...
</template>
File: custom-link.vue
<template>
<a v-text="label"
@click.prevent="downloadItem()">
</template>
<script>
...
methods:{
downloadItem(){
this.$axios.get(...)
.then(response => {
const blob = new Blob([response.data],{ type: response.data.type }) // Here I tried Uint8Array.from(response.data) as well
const link = document.createElement('a')
link.href = window.URL.createObjectURL(blob,{ type: response.data.type });
link.download = this.file_name
link.click()
URL.revokeObjectURL(link.href)
}
...
}
...
}
...
</script>
The problem here is that image is being downloaded. I can see it from devtools network tab. But I just can't link/display it reactively (shown in Step 3).
After Step 5, the file downloads but has a bloated size compared to original. When I convert response.data
to Uint8Array
, the file shrinks compared to original.
It looks like a very simple get and display image problem but haven't got the combination of mimetypes and utilities right. Express supports sendFile and download options for the files. But both the calls don't seem to work for some reason!
Do you have any pointers? Can't we just download the image somewhere on the webserver directory and reactive link it? Even that should be fine with me. Can avoid some API calls.
Upvotes: 1
Views: 4452
Reputation: 493
@coder.in.me's post leads us to the answer - obtain S3 Object's presignedURL.
Added benefit - you can also expire the URL beyond a time-limit.
Upvotes: 1
Reputation: 1068
The following code is taken from https://aws.plainenglish.io/using-node-js-to-display-images-in-a-private-aws-s3-bucket-4c043ed5c5d0 :
function encode(data){
let buf = Buffer.from(data);
let base64 = buf.toString('base64');
return base64
}
getImage()
.then((img)=>{
let image="<img src='data:image/jpeg;base64," + encode(img.Body) + "'" + "/>";
let startHTML="<html><body></body>";
let endHTML="</body></html>";
let html=startHTML + image + endHTML;
res.send(html)
}).catch((e)=>{
res.send(e)
})
Upvotes: 1