create data url from fetched image - javascript

TL;DR
I'm trying to fetch and image, convert it to base64, and put the data url into an img's src attribute, but it's not working:
async function ajax(id) {
const tag = document.getElementById(id);
const path = tag.getAttribute("data-src");
const response = await fetch(path);
const blob = await response.blob();
const base64 = window.btoa(blob);
const content = `data:image/jpeg;base64,${base64}`;
tag.setAttribute("src", content);
}
The details, as well as some other methods, which do work follow.
I have been experimenting with different ways to lazy load:
$ mkdir lazy
$ cd lazy
$ wget https://upload.wikimedia.org/wikipedia/commons/7/7a/Lone_Ranger_and_Silver_1956.jpg # any other example image
now create a file called index.html with this in it:
<script>
// this works
function setAttribute(id) {
const tag = document.getElementById(id);
const path = tag.getAttribute("data-src");
tag.setAttribute("src", path);
}
// this doesn't work for some reason
async function ajax(id) {
const tag = document.getElementById(id);
const path = tag.getAttribute("data-src");
const response = await fetch(path);
const blob = await response.blob();
const base64 = window.btoa(blob);
const content = `data:image/jpeg;base64,${base64}`;
tag.setAttribute("src", content);
}
// this works too
async function works(id) {
const tag = document.getElementById(id);
const path = tag.getAttribute("data-src");
const response = await fetch(path);
const blob = await response.blob();
const content = URL.createObjectURL(blob);
tag.setAttribute("src", content);
}
</script>
set attribute<br />
data url<br />
object url<br />
<img id="example" data-src="Lone_Ranger_and_Silver_1956.jpg"></img><br />
and start a server in that folder:
$ python -m SimpleHTTPServer # or whichever local webserver
and then when I look at it in chrome I get this:
The first and third links both work:
However, the middle link does not:
Here is what the three links do to the tag respectively:
works:
<img id="example" data-src="Lone_Ranger_and_Silver_1956.jpg" src="Lone_Ranger_and_Silver_1956.jpg">
does not work:
<img id="example" data-src="Lone_Ranger_and_Silver_1956.jpg" src="data:image/jpeg;base64,W29iamVjdCBCbG9iXQ==">
works:
<img id="example" data-src="Lone_Ranger_and_Silver_1956.jpg" src="blob:http://localhost:8000/736a9e18-c30d-4e39-ac2e-b5246105c178">
That data url in the non working example also looks too short. So what am I doing wrong?

Thanks for the suggestion #dolpsdw. window.btoa doesn't do what I thought it would. If anybody is trying to do the same thing, instructions for reading a blob into a data url are here: https://stackoverflow.com/a/18650249/5203563
I have created this wrapper that fits right into my program as follows:
(it even adds in the data:image/jpeg;base64, part for you and works out the mime type from the blob)
function readBlob(b) {
return new Promise(function(resolve, reject) {
const reader = new FileReader();
reader.onloadend = function() {
resolve(reader.result);
};
// TODO: hook up reject to reader.onerror somehow and try it
reader.readAsDataURL(b);
});
}
async function ajax(id) {
const tag = document.getElementById(id);
const path = tag.getAttribute("data-src");
const response = await fetch(path);
const blob = await response.blob();
// const base64 = window.btoa(blob);
// const content = `data:image/jpeg;base64,${base64}`;
const content = await readBlob(blob);
tag.setAttribute("src", content);
}
this gives me the much longer data url that I expected:

When you have the inmemory blob
Just generate a url for that blob
var url = urlCreator.createObjectURL(blob)
Then create a new IMG with JavaScript and invoke decode method
const img = new Image();
img.src = url;
img.decode()
.then(() => {
document.body.appendChild(img);
})
.catch((encodingError) => {
// Do something with the error.
})
Maybe you want also to revoke URL after load with
URL.revokeObjectURL(objectURL)
About why the window.btoa does not work, its because is for string to base64 only.
Read about blob to base64 conversión here.
But is a more elegant solution createObjectURL.

Related

Fail to upload local CSV file with fetch() and fail to execute FileReader()

I'm trying to manipulate a local CSV file with JavaScript. My purpose is to display datas from my CSV on my website, like we were using an external API in JSON format for example.
const csvLocalFile =
"http://XXX/.../file.csv";
const openFile = async () => {
const csv = await fetch(csvLocalFile).then();
let reader = new FileReader();
reader.onload = function () {
let text = reader.result;
filecontent = text.replace("", "");
};
reader.readAsText(csv.files[0]);
};
openFile();
Chrome display this error :
TypeError: Cannot read properties of undefined (reading '0')
When I delete "[0]" from "reader.readAsText(csv.files[0])", I have this message error :
TypeError: Failed to execute 'readAsText' on 'FileReader': parameter 1 is not of type 'Blob'.
A empty .then() isn't the problem, turns out it works fine without a fn. but you should remove it regardless.
The FileReader can't read Response objects, only File & Blobs...
You say that you try to read a local file, but yet you use fetch to get a remote file, so what is it that your really trying to do? it's unclear of how to help you...
a csv isn't valid json data, so you can't use .then((res) => res.JSON())
beside res.JSON() is wrong, it should be all lowercased... res.json()
The FileReader is considered legacy so you no longer need it... use await blob.text() instead
here are two examples of how to read 1 remote file using fetch
// Simulate getting a file from eg a file input or drag and drop
const file = new File(['id,name\n10,bob'], 'file.csv', { type: 'text/csv' })
// Simulate a remote location to get the csv from
const url = URL.createObjectURL(file)
const csvLocalFile = url // http://XXX/.../file.csv
const openFile = async () => {
const response = await fetch(csvLocalFile)
const text = await response.text()
console.log(text)
}
openFile()
...and another with a actual local file selected from a user input
const fileInput = document.querySelector('#fileInput')
fileInput.onchange = async () => {
const file = fileInput.files[0]
const text = await file.text()
console.log(text)
}
// ignore code below this line...
// Create a dummy file that we can use to change the file input with...
const dummyFile = new File(['id,name\n10,bob'], 'file.csv', { type: 'text/csv' })
// Used for creating a new FileList in a round-about way
const b = new ClipboardEvent('').clipboardData || new DataTransfer()
b.items.add(dummyFile)
// simulate picking a file
fileInput.files = b.files
fileInput.onchange()
<input type="file" id="fileInput">

Using PDF.js , Is it possible to get link URLs' and its relative coordinates ( positions of X_Y || top_left ) from a pdf file?

Does anyone know any websites with a complete example of how to get coordinates of URL links put in pdf file using PDF.js?
https://mozilla.github.io/pdf.js/
I need data below.
Link URLs of each links.
XY coordinates of each links.
The Size of the page.
ID or name of the page.
I want to put(hover) those links on jpg images which are extracted using pdf.js.
To calculate where to put the links , I need its coordinates.
Thank you.
Here I'm only extracting the links, you can get the extra info from the pdfDocument if you need it.
I'm using linkify to help with the links string parse.
const input = document.getElementById('fileSelector');
const readFile = ()=> {
const file = input.files[0];
const reader = new FileReader();
reader.addEventListener('load', readPDF);
reader.readAsArrayBuffer(file);
};
const readPDF = async (event)=> {
const typedarray = new Uint8Array(event.target.result);
const loadingTask = pdfjsLib.getDocument(typedarray);
const pdfDocument = await loadingTask.promise;
for (let pageNum=1; pageNum<=pdfDocument.numPages; pageNum++) {
readPage(pdfDocument, pageNum);
}
};
const readPage = async (pdfDocument, pageNum)=> {
const page = await pdfDocument.getPage(pageNum);
const textContent = await page.getTextContent();
for (let item of textContent.items) {
const links = linkify.find(item.str);
for (let link of links) appendLink(link);
}
};
const appendLink = (link)=> {
const list = document.getElementById('list');
const a = document.createElement('a');
a.href = link.href;
a.textContent = link.value;
list.appendChild(a);
}
input.addEventListener('change', readFile);
a { display: block }
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/linkifyjs#3.0.3/dist/linkify.min.js"></script>
<input type="file" accept=".pdf" id="fileSelector">
<div id="list"></div>

Reactjs How to download file from Azure Storage Container

I am working on reactjs/typescript applications. I am trying to download some files from azure storage v2. Below is the sample path I am supposed to download files. In this path, enrichment is the container name, and the rest all are folders. I am trying to download the last modified file from reportdocument folder.
enrichment/report/SAR-1234-56/reportdocument/file1.docs
I tried something below.
#action
public async reportDownload(sarNumber: string) {
let storage = globals.getGlobals('StorageAccount03');
console.log(storage);
let containerName = globals.getGlobals('StorageAccount03ContainerName');
let marker = undefined;
let allUploadPromise: Array<Promise<unknown>> = [];
const config = {
path: `/Storage/getsastoken/?storageName=${storage}&containerName=${containerName}`,
method: "GET",
success: (url: any) => {
const containerURL: ContainerURL = new ContainerURL(
url,
StorageURL.newPipeline(new AnonymousCredential()));
const listBlobsResponse = containerURL.listBlobFlatSegment(
Aborter.none,
marker,
);
}
};
await handleRequest(config);
}
From here I am struggling to download the latest modified file from the above path.
can someone help me to fix this? Any help would be greatly appreciated. Thank you
It's better to use #azure/storage-blob library and then the code would be something like below instead of directly trying to call blob REST API like you were trying in your code which seems unnecessary reinventing the wheel. The library already does it for you. Refer this for details.
const { BlobServiceClient } = require("#azure/storage-blob");
const account = "<account name>";
const sas = "<service Shared Access Signature Token>";
const containerName = "<container name>";
const blobName = "<blob name>";
const blobServiceClient = new BlobServiceClient(`https://${account}.blob.core.windows.net${sas}`);
async function download() {
const containerClient = blobServiceClient.getContainerClient(containerName);
const blobClient = containerClient.getBlobClient(blobName);
// Get blob content from position 0 to the end
// In browsers, get downloaded data by accessing downloadBlockBlobResponse.blobBody
const downloadBlockBlobResponse = await blobClient.download();
const downloaded = await blobToString(await downloadBlockBlobResponse.blobBody);
console.log("Downloaded blob content", downloaded);
// [Browsers only] A helper method used to convert a browser Blob into string.
async function blobToString(blob) {
const fileReader = new FileReader();
return new Promise((resolve, reject) => {
fileReader.onloadend = (ev) => {
resolve(ev.target.result);
};
fileReader.onerror = reject;
fileReader.readAsText(blob);
});
}
}
The SAS token expiry bothers me.You cannot have a static SAS token that expires sooner unless we can set long expiry (user-delegation SAS token is short lived). Do we really have the capability to create the SAS token dynamically in javascript runtime? I think it's only possible in NodeJS runtime.

NodeJS + ldapsj-client: problem saving thumbnailPhoto

Using the ldapsj-client module, I'm trying to save the thumbnailPhoto into a file
const auth = async () => {
const client = new LdapClient({ url: 'myaddomain' })
await client.bind('someemail#domain.com.br', 'passwaord')
const opts = {
filter: `(sAMAccountName=credential)`,
scope: "sub"
}
const s = await client.search(myBaseDN, opts)
console.log('thumbnailPhoto', s[0].thumbnailPhoto)
}
The console.log() outputs something like '����JFIF��C...'
I cannot figure out how to save this binary into a file. When I try several approaches, as explained here, does not work. It seems the data from AD is not in the same "format".
I tried to convert it into a Buffer and then, to base64
const buffer = Buffer.from(s[0].thumbnailPhoto, 'binary')
var src = "data:image/png;base64," + Buffer.from(s[0].thumbnailPhoto).toString('base64')
But the output is not a valid base64.

Fetch image from API

Q1) In my reactjs application, I am trying to fetch an API from my backend Nodejs server. The API responds with an image file on request.
I can access and see image file on http://192.168.22.124:3000/source/592018124023PM-pexels-photo.jpg
But in my reactjs client side I get this error on console log.
Uncaught (in promise) SyntaxError: Unexpected token � in JSON at position 0
Reactjs:
let fetchURL = 'http://192.168.22.124:3000/source/';
let image = name.map((picName) => {
return picName
})
fetch(fetchURL + image)
.then(response => response.json())
.then(images => console.log(fetchURL + images));
Nodejs:
app.get('/source/:fileid', (req, res) => {
const { fileid } = req.params;
res.sendFile(__dirname + /data/ + fileid);
});
Is there any better way to do than what I am doing above?
Q2) Also, how can I assign a value to an empty variable (which lives outside the fetch function)
jpg = fetchURL + images;
So I can access it somewhere.
The response from the server is a binary file, not JSON formatted text. You need to read the response stream as a Blob.
const imageUrl = "https://.../image.jpg";
fetch(imageUrl)
// vvvv
.then(response => response.blob())
.then(imageBlob => {
// Then create a local URL for that image and print it
const imageObjectURL = URL.createObjectURL(imageBlob);
console.log(imageObjectURL);
});
Equivalent to solution by #maxpaj, but using async and await.
async function load_pic() {
const url = '<REPLACE-WITH-URL>'
const options = {
method: "GET"
}
let response = await fetch(url, options)
if (response.status === 200) {
const imageBlob = await response.blob()
const imageObjectURL = URL.createObjectURL(imageBlob);
const image = document.createElement('img')
image.src = imageObjectURL
const container = document.getElementById("your-container")
container.append(image)
}
else {
console.log("HTTP-Error: " + response.status)
}
}
This question is 4 years old and I think in 2022 there are many ways to solve this. This is ES6 version using async calls.
First, I don't know if you are trying to download the image or insert the image into a img tag. So I will assume we want to download the image.
The process is simple: a) fetch the image as a blob; b) convert blob to Base64 using URL.createObjectURL(blob); and c) trigger the download using a ghost a tag.
const $btn = document.getElementById('downloadImage')
const url = 'https://s3-ap-southeast-1.amazonaws.com/tksproduction/bmtimages/pY3BnhPQYpTxasKfx.jpeg'
const fetchImage = async url => {
const response = await fetch(url)
const blob = await response.blob()
return blob
}
const downloadImage = async url => {
const imageBlob = await fetchImage(url)
const imageBase64 = URL.createObjectURL(imageBlob)
console.log({imageBase64})
const a = document.createElement('a')
a.style.setProperty('display', 'none')
document.body.appendChild(a)
a.download = url.replace(/^.*[\\\/]/, '')
a.href = imageBase64
a.click()
a.remove()
}
$btn.onclick = event => downloadImage(url)
<button id="downloadImage">Download Image</button>
Note:
StackOverflow uses a sandboxed iframe's so we can test the download but you can use my codepen

Categories

Resources