I have an APIs which returns File(byte[], "application/pdf") | byte[] from server.
In the frontend part, I create a request and can save this file like a pdf file to a computer, but I need to store this file(State or some other way) and render inside react component like PDF file, it's can be put on iframe or some another way(for me it's doesn't matter, just need render inside component after getting the file from API).
const instance = axios.create({
baseURL: process.env.REACT_APP_API_ROOT,
headers: {
"Content-Type": "application/json",
},
});
function getPdfByFileName(fileName: string): any {
return instance.get(`pdfs/${fileName}`);
}
function getPdfAsBlobByFileName(fileName: string): any {
return instance.get(`pdfs/${fileName}/array`);
}
useEffect(() => {
getPdfByFileName("fileName")
.then((response: AxiosResponse) => {
// const file = new Blob([response.data], { type: "application/pdf" }); // For API which returns blob
setPdf(response.data); // Need store file and correct render like PDF
})
.catch((err: AxiosError) => {
// process err
});
}
}, []);
return (
<div>
<iframe title="pdf" width="100%" height="600px" srcDoc={pdf}></iframe>
</div>
);
The result: Inside component I render the data but it's no PDF
I'm tryed:
use "Content-Type": "application/pdf" inside these requests.
Put the URL to file inside Iframe src attribute(it's works fine for
Anonymous but we have Bearer and need to secure the data).
Generate URL from blob API response and put to Iframe(not works).
Different pdf viewers, they work fine with physical PDF files based on the path to file but don't work in my case.
Thanks for any help.
For me the problem is on the axios, the response is not correct, I'm just switched to using fetch method and then this case start work correct for me, the same with axios does not work for me
const request = new Request(`URL_TO_API`,
{
method: "GET",
headers: {
Authorization: `Bearer ${accessToken}`,
},
mode: "cors",
cache: "default",
}
);
fetch(request)
.then((response) => response.blob())
.then((blob) => {
const file = window.URL.createObjectURL(blob);
const iframe = document.querySelector("iframe");
if (iframe?.src) iframe.src = file;
})
.catch((err: AxiosError) => {
// process error
});
return (
<div>
<iframe src="" width="100%" height="100%"></iframe>
</div>
);
Related
I'm new to JavaScript, and am trying to write some code that uses the google drive API (via the gapi client) to transform an existing slide into a pdf document, upload it to a specific folder, and return the pdf file id. This is all to be done in the browser, if possible.
I've already done this on python for another use case, and the code looks something like this:
import googleapiclient.http as client_methods
from io import BytesIO
...
data = drive.files().export(fileId=slideId, mimeType='application/pdf').execute()
body = {'name': fileName, 'mimeType': 'application/pdf', 'parents': [folderId]}
# wrapping the binary (data) file with BytesIO class
fh = io.BytesIO(data)
# creating the Media Io upload class for the file
media_body = client_methods.MediaIoBaseUpload(fh, mimetype='application/pdf')
pdfFileId = drive.files().create(body=body, media_body=media_body, supportsAllDrives=True).execute(['id'])
I've tried to replicate the same steps using JavaScript and my limited knowledge, and can successfully upload a pdf file into the desired folder, but the file shows as empty (doesn't even open in the drive).
I believe it might be due to the way I'm handling the binary data that I get from exporting the initial slide.
The last iteration of my JavaScript code is shown below (I have all the necessary permissions to use the gapi client):
async function createPdfFile() {
gapi.client.load("drive", "v3", function () {
// Set the MIME type for the exported file
const mimeType = "application/pdf";
// Set the file name for the exported PDF file
const fileName = "Trial upload.pdf";
// Export the Google Slides presentation as a PDF file
gapi.client.drive.files.export({
fileId,
mimeType
}).then(async function (response) {
// Get the binary data of the PDF file
const pdfData = await response.body;
const blob = await new Blob([pdfData], {type: 'application/pdf'})
const file = new File([blob], "presentation.pdf");
// Create a new file in the specified Google Drive folder with the PDF data
await gapi.client.drive.files.create({
name: fileName,
parents: [folderId],
mimeType: mimeType,
media: {mimeType: 'application/pdf', body: file},
supportsAllDrives: true
}).then(function (response) {
// Get the ID of the created PDF file
const pdfFileId = response.result.id;
console.log("PDF file created with ID: " + pdfFileId);
})
})
})
}
await createPdfFile()
As for the output, and as stated, it does create a pdf file, and logs the pdf file id, but the file itself is empty. I'd really appreciate it if someone could help me make sense of this (similar thread here, but can't replicate his success).
I believe your goal is as follows.
You want to convert Google Slides to PDF format using googleapis for Javascript.
Your access token can be exported and uploaded to Google Drive.
Issue and workaround:
When I tested your script, unfortunately, response.body from gapi.client.drive.files.export is binary data, and in this case, this cannot be correctly converted to the blob. And also, in the current stage, it seems that a file cannot be uploaded using gapi.client.drive.files.create. I thought that these might be the reason for your current issue.
From these situations, I would like to propose the flow for achieving your goal using fetch API. The modified script is as follows.
In this case, the access token is retrieved from the client like gapi.auth.getToken().access_token.
Modified script:
Please modify your script as follows.
From:
gapi.client.drive.files.export({
fileId,
mimeType
}).then(async function (response) {
// Get the binary data of the PDF file
const pdfData = await response.body;
const blob = await new Blob([pdfData], { type: 'application/pdf' })
const file = new File([blob], "presentation.pdf");
// Create a new file in the specified Google Drive folder with the PDF data
await gapi.client.drive.files.create({
name: fileName,
parents: [folderId],
mimeType: mimeType,
media: { mimeType: 'application/pdf', body: file },
supportsAllDrives: true
}).then(function (response) {
// Get the ID of the created PDF file
const pdfFileId = response.result.id;
console.log("PDF file created with ID: " + pdfFileId);
})
})
To:
gapi.client.drive.files.get({ fileId, fields: "exportLinks", supportsAllDrives: true }).then(function (response) {
const obj = JSON.parse(response.body);
if (Object.keys(obj).length == 0) throw new Error("This file cannot be converted to PDF format.");
const url = obj.exportLinks["application/pdf"];
if (!url) throw new Error("No exported URL.");
const accessToken = gapi.auth.getToken().access_token;
fetch(url, {
method: 'GET',
headers: { 'Authorization': 'Bearer ' + accessToken },
})
.then(res => res.blob())
.then(blob => {
const metadata = { name: fileName, parents: [folderId], mimeType };
const form = new FormData();
form.append('metadata', new Blob([JSON.stringify(metadata)], { type: 'application/json' }));
form.append('file', blob);
fetch('https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&supportsAllDrives=true', {
method: 'POST',
headers: { 'Authorization': 'Bearer ' + accessToken },
body: form
})
.then(res => res.json())
.then(obj => console.log("PDF file created with ID: " + obj.id));
});
});
When this script is run, the export URL of PDF data is retrieved from the file ID. And, the PDF data is downloaded and uploaded to Google Drive.
Note:
In your script, fileId is not declared. Please be careful about this.
If the file size is more than 5 MB, please use the resumable upload.
Reference:
Upload file data
Added:
From your following reply,
?uploadType=multipart also returns a 404 type error
I'm worried about that in your situation, new FormData() might not be able to be used. If my understanding is correct, please test the following script. In this script, the request body of multipart/form-data is manually created.
Modified script:
gapi.client.drive.files.get({ fileId, fields: "exportLinks", supportsAllDrives: true }).then(function (response) {
const obj = JSON.parse(response.body);
if (Object.keys(obj).length == 0) throw new Error("This file cannot be converted to PDF format.");
const url = obj.exportLinks["application/pdf"];
if (!url) throw new Error("No exported URL.");
const accessToken = gapi.auth.getToken().access_token;
fetch(url, {
method: 'GET',
headers: { 'Authorization': 'Bearer ' + accessToken },
})
.then(res => res.blob())
.then(blob => {
const metadata = { name: fileName, parents: [folderId], mimeType };
const fr = new FileReader();
fr.onload = e => {
const data = e.target.result.split(",");
const req = "--xxxxxxxx\r\n" +
"Content-Type: application/json\r\n\r\n" +
JSON.stringify(metadata) + "\r\n" +
"--xxxxxxxx\r\n" +
"Content-Transfer-Encoding: base64\r\n\r\n" +
data[1] + "\r\n" +
"--xxxxxxxx--";
fetch('https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&supportsAllDrives=true', {
method: 'POST',
headers: { 'Authorization': 'Bearer ' + accessToken, "Content-Type": "multipart/related; boundary=xxxxxxxx" },
body: req
})
.then(res => res.json())
.then(obj => {
console.log("PDF file created with ID: " + obj.id)
});
}
fr.readAsDataURL(blob);
});
});
When I tested this script, no error occurs. I confirmed that the Google Slides file could be converted to a PDF file and the PDF file was uploaded to the specific folder.
I would like to know how to share a base64 string as an image over WhatsApp primarily.
I have a react app that generates a QR code and renders it using <img src={base64String} />. Now that img is wrapped around an <a><a/> tag with download attribute, it is working fine. But what I want to accomplish is to be able to have a share button that can share the image on WhatsApp.
I don't want to store it locally or involve file reading if possible because I had a lot of issues implementing it since the QR code will be discarded after download and need not be stored as a file. But if that is the right way I'm ok to work with it.
I tried creating a blob and sharing by using atob(), but that returned an error saying "string not encoded properly" which I didn't want to dig into since it was able to render it as an image and also download by which I assumed the base64 string was correct.
code for Generator:
const GenerateQRCode = (e) => {
e.preventDefault()
QRCode.toDataURL(value, {
margin: 2,
color: {
dark: '#000000',
light: '#ffffff'
},
width: 2000,
height:2000
}, (err, value) => {
if (err) return console.error(err)
console.log(value)
setQr(value)
console.log(data);
})
}
const handleShare =(e)=>{
}
code for that renders the image:
{qr && <>
<img src={qr} alt='' className='qrimg' id='can'/>
<a href={qr} download={`${state.name}.png`} ><button value="Download" >Download</button></a>
</>
}
<button type="" onClick={handleShare}></button>
So, I want that share button to send an image over WhatsApp. My previous attempts for handleShare were messy so I wanted to get fresh inputs or a Solution to do this the right way.
Finally success, that was a hard one! You have to upload the file as a Media into the API and then use that uploaded Media ID to send. Check out the code and replace "xxx" with your values.
Note: Your QR code must be a PNG for this to work. This is not a snippet because it doesn't make sense to run
<img src="data:image/png;base64,iVBORw0KGg...VERY_LONG_STRING_HERE...">
<script>
// Your auth token. Keep it scret, keep it safe.
const Authorization = "Bearer xxx"
const type = 'image/png' // Image type (cannot be gif due to API restriction!)
const baseUrl = "https://graph.facebook.com/v15.0/xxx" // Phone number of sender
const recipient = "xxx" // Phone number of recipient
/** This function creates a File object from an img element which has a base64 image */
function fileFromImg(img) {
var byteString = atob(img.src.split(',')[1]);
var arrayBuffer = new ArrayBuffer(byteString.length);
var arr = new Uint8Array(arrayBuffer);
for (var i = 0; i < byteString.length; i++) {
arr[i] = byteString.charCodeAt(i);
}
const blob = new Blob([arrayBuffer], { type });
return new File([blob], "qr.png", { type })
}
/** This function will upload a File and call the given callback with the result */
function sendFile(f, callback) {
const url = baseUrl + "/media";
const formData = new FormData()
formData.append('file', f)
formData.append('type', type)
formData.append('messaging_product', "whatsapp")
fetch(url, {
method: 'POST',
body: formData,
headers: { Authorization }
})
.then(r => r.json())
.then(callback)
.catch(console.error)
}
/** This takes in a successful upload and sends that forward to receiver */
function handleResponse(response) {
// When it works response will look like {"id": "xxx"}
fetch(baseUrl + "/messages", {
method: 'POST',
body: JSON.stringify({
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": recipient,
"type": "image",
"image": response
}),
headers: { Authorization, "Content-Type": "application/json" }
})
.then(r => r.json())
.then(r => console.log(r))
.catch(console.error)
}
// I choose to trigger the sending when anything is clicked
document.addEventListener("click", () => {
const img = document.querySelector("img")
const file = fileFromImg(img)
sendFile(file, handleResponse)
})
</script>
The recipient will see this;
I am trying to code POST REST API request as shown in the above Postman screenshot.
Here is my code:
import { fromFetch } from 'rxjs/fetch';
import { switchMap } from 'rxjs/operators';
interface FetchRequest<TBody> {
method?: FetchMethod;
body?: TBody;
headers?: FetchHeaders;
isBlobRequest?: boolean;
}
export const request = <TBody extends BodyInit = BodyInit>(
url: string,
fetchRequest?: FetchRequest<TBody>,
contentType: string
) => {
const { method = 'POST', body, headers } = fetchRequest ?? {};
const { dispatch } = createStore();
let request = new Request(`${domainUrl}${url}`, {
method,
body: body ? body : null,
headers: {
...headers,
'Content-type': contentType ,
},
credentials: 'include',
});
return fromFetch(request).pipe(
switchMap((response: Response) => {
if (response.status === SUCCESS_CODE) {
if (isBlobRequest) {
return response.blob();
}
return response.text();
} else if (response.status === USER_SESSION_EXPIRED_CODE) {
dispatch(authAsyncActions.setSessionExpired());
throw response;
} else {
// This triggers error-handling and allows us to inspect the entire
// error response including its status and body
throw response;
}
})
);
};
const callUploadAPI = (file) => {
let formData = new FormData();
formData.append('file', file);
request(`urlUploadFile`, { method: 'POST', body: formData}, 'application/vnd.ms-excel')
}
In above code I am using fromFetch of "rxjs/fetch" to call the POST REST API and passing "Request" object in fromFetch().
Request interface is inside typescript as per the below screenshot.
Backend is Python flask server and on the backend side in Python code I am using file = request.files['file'] to get the file which is working when I call the API through Postman but when I call the API through frontend code it is not getting file.
How can I set the Request "Body" type as "form-data" and "KEY" type as File in frontend code?
You're explicitly setting the Content-type to application/vnd.ms-excel, but you don't need to set this header
As far as I know, if the body of a fetch request is a FormData object, the browser automatically sets the content type header to multipart/form-data
I am changing my form submission to make it more fluid via using fetch.
In order to process the value of my input image:
<input name="perfil" type='file' id="imageUpload />
And then, in order to upload it to Amazon S3,
I do this in my views.py:
if request.method == "POST"
image = request.FILES['perfil']
im = Image.open(image)
output = BytesIO()
rgb_im = im.convert('RGB')
rgb_im.save(output, format='JPEG', quality=90)
output.seek(0)
s3.Bucket('bucketname').upload_fileobj(output, request.user.email + '.profileImage')
But now (because i'm trying to implement fetch), I am getting the image file like this:
fetch(`url`, {
method: 'POST',
body: JSON.stringify({
image: document.querySelector('#imageUpload').files[0],
}),
headers: {
"Content-type": "application/json; charset=UTF-8",
"X-CSRFToken": getCookie('csrftoken')
}
})
}
The problem is that when I do request.body['image`] in the server (views.py), all I'm getting is this: "image":{}
And I don't know how to process this file in JS before I send it to the server (that will end up uploading it to amazon s3)
With this Example you can upload Images on a Server!
// Select your input type file and store it in a variable
const input = document.getElementById('fileinput');
// This will upload the file after having read it
const upload = (file) => {
fetch('http://www.example.net', { // Your POST endpoint
method: 'POST',
headers: {
// Content-Type may need to be completely **omitted**
// or you may need something
"Content-Type": "You will perhaps need to define a content-type here"
},
body: file // This is your file object
}).then(
response => response.json() // if the response is a JSON object
).then(
success => console.log(success) // Handle the success response object
).catch(
error => console.log(error) // Handle the error response object
);
};
// Event handler executed when a file is selected
const onSelectFile = () => upload(input.files[0]);
// Add a listener on your input
// It will be triggered when a file will be selected
input.addEventListener('change', onSelectFile, false);
I want make a request to API with headers having UserID:Pass
Example :
const config = {
headers: {
'X-RPC-DIRECTORY': 'main',
'X-RPC-AUTHORIZATION': 'userid:pass'
}
};
const res = await axios.get(`http://192.00.00.60:8000/obj/e1b8c19e-fe8c-43af-800c-c9400c0e90/abc.jpg`, config );
How can I render this?
Using the same I can get the image in Postman, But I want to render this into the page.
1- Create useState to save your base64 data.
const [base64, setBase64] = useState();
2- Create useEffect or function to transform image from get request to base64.
useEffect(() => {
axios
.get(
url,
{
responseType: "arraybuffer",
}
)
.then((response) =>
setBase64(Buffer.from(response.data, "binary").toString("base64"))
);
}, []);
3- Display the base64 data as image according to the syntax of the data URI scheme:
<img src={data:[<media type>][;charset=<character set>][;base64],<data>} />
example:
<img src={`data:image/jpeg;charset=utf-8;base64,${base64}`} />
axios({
method:'get',
url,
auth: {
username: 'xxxxxxxxxxxxx',
password: 'xxxxxxxxxxxxx'
}
})
.then((response) => {
//From here you can pass the response to local variable(state) and store/show image.
this.setState({ imageURL : response.data.image }); // change this as per your response
})
.catch((error) => {
console.log(error);
});
render(){
return(
<React.Fragment>
<img src={this.state.imageURL} alt="image" />
</React.Fragment>
)
}
Make sure you have right method type, URL and data is coming in response.
Got the Solution, as the response was content-type: blob so, what I did is to convert the blob to base64 from FileReader api and then display it.
const fileReaderInstance = new FileReader();
fileReaderInstance.readAsDataURL(blob);
fileReaderInstance.onload = () => {
base64data = fileReaderInstance.result;
console.log(base64data);
}
class Hello extends Component {
state = {
ulr: ''
}
componentDidMount() {
const config = {
headers: {
'X-RPC-DIRECTORY': 'main',
'X-RPC-AUTHORIZATION': 'userid:pass'
}
};
axios.get(`http://192.00.00.60:8000/obj/e1b8c19e-fe8c-43af-800c-c9400c0e90/abc.jpg`, config ).then((response) => {
this.setState({ url: response.data })
}).catch((error) => {
console.log(error)
})
}
render() {
return (
<>
<img src={this.state.url} alt="#" />
</>
)
}
}
export default Hello;
This should answer how to render the image after fetching the image from API.
But what i think is something is wrong withe URL.
i have few question:
Is the back-end API hosted on our own system?
Is the API publicly available because if it's not we cannot access it.
Have you set ant other headers or params that needs to be sent along the request.
I tried with postman also didn't get the image in response, it gave me error.