I'm trying to upload a static file from within my JS code. I have a .tgz file that I want to send as POST parameter. I've dumped my file to a hex string fileDatalike:
var file = "\xff\x01\x08 .....
I want to send as the body content of a multi form post data. It seems like when I try to just .send(file) the hex values become mangled and the file is corrupted.
JS looks something like this:
var url = "/index/upload/"
var fileData = '\xF1\x08\x00\x00........\x00\x00';
//whole file binary content in hex)
boundary = "----------3333338220322",
xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "multipart/form-data, boundary="+boundary);
var body = "--" + boundary + "\r\n";
body += 'Content-Disposition: form-data; name="newFile"; filename="filename.tgz"\r\n';
body += "Content-Type: application/x-gzip\r\n\r\n";
body += fileData + "\r\n";
body += "--" + boundary + "--";
xhr.send(body);
Not sure what goes wrong. The request seems to look exactly like if I manually upload the file, but the binary data seems different when observing through a proxy. Is there a better way to send files if i need to have its whole content in the JS code itself?
Figured out a wait to make it work. Using base64 to encode the file instead of hex.
var b64data = 'H4sIAHegu.....';
const b64toBlob = (b64Data, contentType='', sliceSize=512) => {
const byteCharacters = atob(b64Data);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
const slice = byteCharacters.slice(offset, offset + sliceSize);
const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
const blob = new Blob(byteArrays, {type: contentType});
return blob;
}
const contentType = 'application/x-gzip';
const blob = b64toBlob(b64data, contentType);
Then used this blob as the file content.
Solution found from here:
Convert base64 png data to javascript file objects
offsec/awae ;)
Related
I am getting the response from rest api as:
<Response>
<PDFContent>SlZCRVJpMHhMalFLSmVMano5TUtNU0F3SUc5aWFnbzhQQzlUTDBwaGRtRlRZM0pwY0hRdlNsTW9kR2hwY3k1NmIyOXRJRDBnTVRBd095aytQZ3BsYm1Sdlltb0tOU0F3SUc5aWFnbzhQQzlEYjJ4dmNsTndZV05sTDBSbGRtbGpaVWR5WVhrdlUzVmlkSGx3WlM5SmJXRm5aUzlJWldsbmFIUWdNVFV3TDBacGJIUmxjaTlHYkdGMFpVUmxZMjlrWlM5VWVYQmxMMWhQWW1wbFkzUXZWMmxrZEdnZ056Y3lMMHhsYm1kMGFDQTNNek0zTDBKcGRITlV3cjFTdkgvd0JvNy9rbm1uLzloV1AvQU5GUzBBZUFmOEozNHcvNkd2WFAvQmpOL3dERlVmOEFDZCtNUCtocXhycFg5L2V5ZlRZRURvYTRmdUNBNWNDQjFVSjJEd005cWwyZE9LbzlOSDVhVXdkdjVTVFRYbkMwVWVZbnlyVE5N6bnRLaXRSWHV1REUxUHNSL2lGQ2FpdGhvTUlacGQ0NmxMU0d3aVdZSkZoVVVaRm5KbnN5NHpPWE9BZS94ZDEIyOTBJREl6SURBZ1VpOVRhWHBsSURJMVBqNEtjM1JoY25SNGNtVm1DakUzT1RjMU5Rb2xKVVZQUmdvPQ==</PDFContent>
<Success>True</Success>
</Response>
How to get the response inside and generate pdf using React js
you can simply add in object tab in reactjs
<div>
<object
style={{width: '100%', height: '200pt'}}
type="application/pdf"
data={'data:application/pdf;base64,'+base64data}></object>
</div>
I am assuming the bytes within <PDFContent> tag are base64 encoded bytes of the PDF file. If this is incorrect, you need to update your REST API to perform base64 encoding on the PDF bytes first. You can't have your server send raw PDF bytes to the web client as is.
In the javascript, once you have received the base64 encoded PDF bytes, you can generate the PDF in two steps:
Convert the base64 encoded bytes to a PDF BLOB.
Create a file URL out of this BLOB and open the URL.
Here's a sample JavaScript code for the same:
<script>
function displayPDF() {
//The PDFContent bytes are mapped to an input element called pdfString
if($('#pdfString').val() == '')
return;
var file = b64toBlob($('#pdfString').val(), 'application/pdf');
//If the browser is IE or Edge
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(file);
}
else {
var fileURL = URL.createObjectURL(file);
window.open(fileURL);
}
}
function b64toBlob(b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, {type: contentType});
return blob;
}
</script>
I'm trying to pass a PDF file from the server and display it inside the browser, but the output comes out corrupted.
var blob = atob(data.Package);
console.log(blob);
var file = new Blob([blob], { type: "application/pdf" });
const fileURL = URL.createObjectURL(file);
window.open(fileURL);
Console log outputs something that appears to be correct PDF (just the beginning of output):
I'm saving a copy of the PDF on the server before transfer to make sure it is not corrupt and it works.
URL constructed with URL.createObjectURL(file) seems to be short:
blob:http://localhost:61631/ad749684-2992-4311-8b17-f382a7c687be
server side code:
Object doc = Convert.ToBase64String(_Document.DocumentStream.ToArray());
JObject response = new JObject(new JProperty("Package", JObject.FromObject(doc)));
return new AspResponse<Object>(response);
It looks like the issue is because you need to convert the PDF data into an actual byte array, then pass that into the Blob constructor. Try this:
function convertToByteArray(input) {
var sliceSize = 512;
var bytes = [];
for (var offset = 0; offset < input.length; offset += sliceSize) {
var slice = input.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
bytes.push(byteArray);
}
return bytes;
}
var blob = atob(data.Package);
console.log(blob);
var file = new Blob(convertToByteArray(blob), { type: "application/pdf" });
const fileURL = URL.createObjectURL(file);
window.open(fileURL);
This solution worked for me, basically generating the pdf as a base64 string in the backend and rendering the content in an anchor tag for downloading the pdf file.
https://kainikhil.medium.com/nodejs-how-to-generate-and-properly-serve-pdf-6835737d118e
I'm using a webservice to get a base64 string and I need to show that document to the user as a PDF.
var charactersArray = atob(base64String);
var byteNumbers = new ArrayBuffer(charactersArray.length);
for (var i = 0; i < charactersArray.length; i++) {
byteNumbers[i] = charactersArray.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
var file = new File([byteArray], "file.pdf", {
type: "application/pdf",
});
I'm then using this "file" to create a url with
var url = URL.createObjectURL(file);
I'm opening this url in a button with the ng-click directive, but I'm getting loading the PDF.
You need to write the character codes to the byteArray rather than the ArrayBuffer
var charactersArray = atob(base64String);
var len = charactersArray.length;
var byteNumbers = new ArrayBuffer(len);
var byteArray = new Uint8Array(byteNumbers);
for (var i = 0; i < len; i++) {
byteArray[i] = charactersArray.charCodeAt(i);
}
var file = new File([byteArray], "file.pdf", {
type: "application/pdf",
});
I recently work on a project like this and had the same issue. I used the base64-arraybuffer NPM library to convert a base64 string to a byte array.
It's a JS library so it needs to be imported like this after it's installed:
import * as buffer from 'base64-arraybuffer';
The object URL is created like this:
var byteArray = buffer.decode(base64String);
var file = new Blob([byteArray], {type: 'application/pdf'});
var pdfUrl = URL.createObjectURL(file);
I hope this helps!
There must be a way to do this more efficiently. What I'm doing is conceptually very simple:
1) Call a web service and receive b64-encoded-string of a PDF blob.
2) Decode it, create blob, render PDF in new window. Account for pop-up blocker.
My code works. Nothing fancy. Its all client-side. Everything works but IE runs SUPER slow compared to the other browsers (IE 11 vs. current Chrome/Firefox/Safari).
In light of this I am certain I could do this more efficiently. Any tips on how to speed this up for IE 11?
Note: I'm using Jeremy's b64toBlob function (thanks Jeremy).
Part I: modal stuff
var box = new SimpleDialog(Dialogs.getNextId(), false);
box.title = "Fetching PDF";
box.isMovable = false;
box.extraClass = "";
box.width = 400;
box.isModal = true;
box.createDialog();
window.parent.box = box;
box.setContentInnerHTML('<p>Please wait....</p>');
box.show();
Part II: call external service, receive b64 encoded string
setTimeout(function(){
var response = ... ; //do callout... get data
var statusCode = ...; //parse from response
var b64Data = ... ; //parse from response
if(statusCode == 200) {
//Account for IE
if (navigator.appVersion.toString().indexOf('.NET') > 0) {
var blob = b64toBlob(b64Data, "application/pdf");
var fileURL = URL.createObjectURL(blob);
window.navigator.msSaveOrOpenBlob(blob, "theFile.pdf");
window.parent.box.cancel();
} else {
var blob = b64toBlob(b64Data, "application/pdf");
var fileURL = URL.createObjectURL(blob);
var pdfWin = window.open(fileURL,"_blank","width=1000,height=800");
if(!pdfWin) {
box.setTitle("Success: PDF has been retrieved");
box.setContentInnerHTML("<p align='left'></p><p align='left'>A popup blocker was detected. The PDF will not open automatically.<br /><br /></p><p align='left'><a onclick='window.parent.box.cancel();' target='_blank' href='"+fileURL +"' >Click here to view .pdf</a><br /><br /></p><p align='center'><button class='btn' onclick='window.parent.box.cancel(); return false;'>Cancel</button></p>");
} else {
window.parent.box.cancel();
}
}
} else {
box.setTitle("Error fetching PDF");
box.setContentInnerHTML("<p align='left'><img src='/img/msg_icons/warning32.png' style='margin:0 5px;'/></p><p align='left'>Unable to retrieve PDF.</p><p align='center'><button class='btn' onclick='window.parent.box.cancel(); return false;'>OK</button></p>");
}
},200);
I don't really see any slowness, and this plunkr run in IE, (using an update on the original "Jeremy" solution) works just fine:
Sample pdf
There was an update in the original post that improves the answer further:
function base64toBlob(base64Data, contentType, sliceSize) {
var byteCharacters,
byteArray,
byteNumbers,
blobData,
blob;
contentType = contentType || '';
byteCharacters = atob(base64Data);
// Get blob data sliced or not
blobData = sliceSize ? getBlobDataSliced() : getBlobDataAtOnce();
blob = new Blob(blobData, { type: contentType });
return blob;
/*
* Get blob data in one slice.
* => Fast in IE on new Blob(...)
*/
function getBlobDataAtOnce() {
byteNumbers = new Array(byteCharacters.length);
for (var i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
byteArray = new Uint8Array(byteNumbers);
return [byteArray];
}
/*
* Get blob data in multiple slices.
* => Slow in IE on new Blob(...)
*/
function getBlobDataSliced() {
var slice,
byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
slice = byteCharacters.slice(offset, offset + sliceSize);
byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
byteArray = new Uint8Array(byteNumbers);
// Add slice
byteArrays.push(byteArray);
}
return byteArrays;
}
}
From the answer here:
martinoss answer
Is the plunkr slow for you? Can you put in some logging to understand which call is actually slow? Put in a timer and log each line. on the IE route. Which one is reporting "slowness"?
Update On the plunkr, I've put a very simple timer, it shows that there is just 46ms approx taken to get the PDF to you in IE11. Obviously it's not multithreaded, but it is an indication.
I have got base64data from ng-src, but I can't make a jpeg file with this base64data. I have ever been trying as follow:
First I have removed the data:image/jpeg:base64, chunk from data:image/jpeg:base64,/9/DEefesdfae453/sdDFEYEUdfdief8fe ... and write this data with test.jpg file by use of $cordovaFile. Here is my code:
var base64Image = angular.element(document.querySelectorAll('#my-photo')).attr('src');
var writeData = base64Image.replace(/data:image\/jpeg;base64,/g, '');
alert(writeData);
// Writing filtered image -----------------------------------------------------------------
$cordovaFile.writeFile(ROOT_DIR + PM_DATA_DIR_NAME + '/', $scope.photo.name, writeData, true)
.then(function (success) {
// success
$ionicLoading.hide();
$ionicPopup.alert({
template : "Updated Photo!!"
});
}, function (error) {
// error
$ionicLoading.hide();
$ionicPopup.alert({
template : "Failed to update Photo!!"
});
});
But I can't show the file as image file. It is showing only black screen. (original file size : 19K ---> result file size : 66K)
What's wrong?
Please help me.
Thank you everybody to help.
FileWriter’s write method does not take a base64 string. According to the docs (http://docs.phonegap.com/en/edge/cordova_file_file.md.html#FileWriter) text will be encoded as UTF-8 before being written. So your base64 string is being encoded before writing to the file so it’s not valid image data. You have to pass your image data as a Blob or an ArrayBuffer. Note this only works on iOS and Android. Have a look at Jeremy Banks’ b64toBlob function in this answer: https://stackoverflow.com/a/16245768
function b64toBlob(b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, {type: contentType});
return blob;
}
You can pass the resulting blob into the write method.