I'm looking to pass a chart export to a component that specifically requires PNG or JPG- so an SVG will unfortunately not work.
With the help of other SO answers- I was able to get the SVG Base64 using the following code:
let link = "data:image/svg+xml;base64," + btoa(this.lineChartRef.current.CHART.current.chart.getSVG());
Is there a way I can get the PNG base64? Or is there an efficient way within React to convert this SVG base64 to a PNG?
Thanks!!!
Thanks to #ppotaczek for his forwarding of [this SO post][1], I was able to create the following solution for React. Hopefully it will help someone else in the future!
//Start by calling the chart options (using refs) into a variable and JSON stringify
let chartoptions = this.lineChartRef.current.BrokerChart.current.chart.userOptions
chartoptions = JSON.stringify(chartoptions)
//Create a data body with the following parameters
let data = {
options: chartoptions,
filename: 'LineChart',
async: true
}
let url = "https://export.highcharts.com/"
let returnedimageurl = ""
//setState will be called within the Axios return so "This" must
let self = this
axios({
method: 'post',
url: 'https://export.highcharts.com/',
data: data,
})
.then(function (response) {
returnedimageurl= url +response.data
self.setState({
activityChartPng: url
}, self.allowDownload())
})
.catch(function (response) {
//handle error
console.log(response);
});
//The activityChartPvg state can then be passed as props to the React-PDF component
[1]: https://stackoverflow.com/questions/54761784/highcharts-export-multiple-charts-using-jspdf
Related
I seriously confused how to solve this multipart boundary when using Axios, react.js and multipart/formdata. I already stuck for 2 weeks to try to solve this but I feel like I getting closer to solved it but it still stuck no matter what solution I try.
I read and trysome solution from this topic:
how-to-post-multipart-formdata-using-fetch-in-react-native
how-to-send-multipart-form-data-with-antd-upload-react
how-to-send-a-multipart-form-data-from-react-js-with-an-image
this is my create Order function in orderAction.js :
function createOrder(data) {
return dispatch => {
let apiEndpoint = 'order';
let payload = new FormData();
// payload.append('orderImage', data.orderImage);
// console.log("Cek Image : ", data.orderImage);
for (const file of data.orderImage) {
payload.append('orderImage', file)
}
payload.append('userId', data.userId);
payload.append('materialId', data.materialId);
// payload.append('materialId', '5d79930c8c4a882f44b1b0fb');
payload.append('color', data.color);
payload.append('description', data.description);
payload.append('quantity', data.quantity);
payload.append('city', data.city);
payload.append('detailAddress', data.detailAddress);
console.log("Cek Data : ", payload);
fetch(config.baseUrl + apiEndpoint, {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('token'),
'Content-Type' : 'multipart/form-data; boundary=----WebKitFormBoundaryHl8DZV3dBSj0qBVe'
},
body: payload
})
// orderService.post(apiEndpoint, payload)
// .then(res => {
// if(res.data.status === 200) {
// alert(res.data.Message);
// dispatch(createOrderSuccess(res.data));
// history.push('/user-order');
// } else {
// dispatch(createOrderFailed());
// alert(res.data.Message);
// }
// })
};
}
can someone help me to solve this? I'm quite confused with this problem
Edit 1
after try using #narasimha solution finally I got rid the multipart boundary but I got weird behaviour where The data succesfully got encoded like this:
but When I trying check the response the photoUrl return null or `` like this:
and when I try using insomnia or postman it successfully generated the photoUrl like this:
where did I wrong in here?
I had the same problem yesterday. The problem was with content type. I had used the same content type header as you are using. The thing is I removed content type and allowed the fetch () API to handle it automatically. It worked!!
THE SITUATION:
Frontend: Vue. Backend: Laravel.
Inside the web app I need to let the user download certain pdf files:
I need Laravel to take the file and return it as a response of an API GET request.
Then inside my Vue web app I need to get the file and download it.
THE CODE:
API:
$file = public_path() . "/path/test.pdf";
$headers = [
'Content-Type' => 'application/pdf',
];
return response()->download($file, 'test.pdf', $headers);
Web app:
downloadFile() {
this.$http.get(this.apiPath + '/download_pdf')
.then(response => {
let blob = new Blob([response.data], { type: 'application/pdf' })
let link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = 'test.pdf'
link.click()
})
}
OUTCOME:
Using this code I do manage to download a pdf file. The problem is that the pdf is blank.
Somehow the data got corrupted (not a problem of this particular pdf file, I have tried with several pdf files - same outcome)
RESPONSE FROM SERVER:
The response itself from the server is fine:
PDF:
The problem may be with the pdf file. It definitely looks corrupted data. This is an excerpt of how it looks like the response.data:
THE QUESTION:
How can I properly download a pdf file using Laravel for the API and Vue for the web app?
Thanks!
SOLUTION:
The code above was correct. What was missing was adding the proper responseType as arraybuffer.
I got scared by those ???? inside the response, and that was misleading me.
Those question marks were just okay since pdf is a binary data and is meant to be read by a proper reader.
THE ARRAYBUFFER:
And arraybuffer is precisely used to keep binary data.
This is the definition from the mozilla website:
The ArrayBuffer object is used to represent a generic, fixed-length
raw binary data buffer. You cannot directly manipulate the contents of
an ArrayBuffer; instead, you create one of the typed array objects or
a DataView object which represents the buffer in a specific format,
and use that to read and write the contents of the buffer.
And the ResponseType string indicates the type of the response. By telling its an arraybuffer, it then treats the data accordingly.
And just by adding the responseType I managed to properly download the pdf file.
THE CODE:
This is corrected Vue code (exactly as before, but with the addition of the responseType):
downloadFile() {
this.$http.get(this.appApiPath + '/testpdf', {responseType: 'arraybuffer'})
.then(response => {
let blob = new Blob([response.data], { type: 'application/pdf' })
let link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = 'test.pdf'
link.click()
})
}
EDIT:
This is a more complete solution that take into account other browsers behavior:
downloadContract(booking) {
this.$http.get(this.appApiPath + '/download_contract/' + booking.id, {responseType: 'arraybuffer'})
.then(response => {
this.downloadFile(response, 'customFilename')
}, response => {
console.warn('error from download_contract')
console.log(response)
// Manage errors
}
})
},
downloadFile(response, filename) {
// It is necessary to create a new blob object with mime-type explicitly set
// otherwise only Chrome works like it should
var newBlob = new Blob([response.body], {type: 'application/pdf'})
// IE doesn't allow using a blob object directly as link href
// instead it is necessary to use msSaveOrOpenBlob
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(newBlob)
return
}
// For other browsers:
// Create a link pointing to the ObjectURL containing the blob.
const data = window.URL.createObjectURL(newBlob)
var link = document.createElement('a')
link.href = data
link.download = filename + '.pdf'
link.click()
setTimeout(function () {
// For Firefox it is necessary to delay revoking the ObjectURL
window.URL.revokeObjectURL(data)
}, 100)
},
You won't be able to do the download from Laravel to Vue since both are running at different ports I assume.
Even if you try something like this.
public function getDownload()
{
//PDF file is stored under project/public/download/info.pdf
$file= public_path(). "/download/info.pdf";
$headers = [
'Content-Type' => 'application/pdf',
];
return response()->download($file, 'filename.pdf', $headers);
}
It won't help as you are sending headers to the Laravel Port Try using Vue js libraries and try to send that pdf content on the library
Try this
Get help from here
it's works for me.
from laravel backend:
$pdf = PDF::loadView('your_view_name', ['data' => $data]);
return $pdf->output();
from vuejs frontend:
axios({
url: 'http://localhost:8000/api/your-route',
method: 'GET',
responseType: 'blob',
}).then((response) => {
var fileURL = window.URL.createObjectURL(new Blob([response.data]));
var fileLink = document.createElement('a');
fileLink.href = fileURL;
fileLink.setAttribute('download', 'file.pdf');
document.body.appendChild(fileLink);
fileLink.click();
});
downloadFile: function () {
this.$http.post('{{ route('download.download') }}', {
_token: "{{ csrf_token() }}",
inputs: this.inputs
},{responseType: 'arraybuffer'}).then(response => {
var filename = response.headers.get('content-disposition').split('=')[1].replace(/^\"+|\"+$/g, '')
var url = window.URL.createObjectURL(new Blob([response.body],{type:response.headers.get('content-type')}))
var link = document.createElement('a')
link.href = url
link.setAttribute('download', filename)
document.body.appendChild(link)
link.click()
});
},
Currently I have this, if with the full app it will create a post with my chosen parameters, however I am very new with vue.js, My aim is to be able to have a text file of such (or other way of storing (json etc)) the values, and then having the js script iterate through the file and display as cards, so for example in the file I would have
"Mark", "http://google.com", "5556", "image"
Or of course using json or similar, I'm up to what ever but my problem is, I don't know how to get values from a remote source and mirror it on to the document, can anyone help?, for clarity here's the snippet of code that I'm using
var app = new Vue({
el: '#app',
data: {
keyword: '',
postList: [
new Post(
'Name',
'Link',
'UID',
'Image'),
]
},
});
-- EDIT --
I'd like to thank the user Justin MacArthur for his quick answer, if you or anyone else doesn't mind answering another one of my painfully incompetent questions. This is the function that adds the cards in a nutshell
var Post = function Post(title, link, author, img) {
_classCallCheck(this, Post);
this.title = title;
this.link = link;
this.author = author;
this.img = img;
};
I can now get the data from the text file, meaning I could do, and assuming I have response defined (that being the http request) it'll output the contents of the file, how would I do this for multiple cards- as, as one would guess having a new URL for each variable in each set of four in each card is not just tedious but very inefficient.
new Post(
response.data,
)
The solution you're looking for is any of the AJAX libraries available. Vue used to promote vue-resource though it recently retired that support in favor of Axios
You can follow the instructions on the github page to install it in your app and the usage is very simple.
// Perform a Get on a file/route
axios.get(
'url.to.resource/path',
{
params: {
ID: 12345
}
}
).then(
// Successful response received
function (response) {
console.log(response);
}
).catch(
// Error returned by the server
function (error) {
console.log(error);
}
);
// Perform a Post on a file/route
// Posts don't need the 'params' object as the second argument is sent as the request body
axios.post(
'url.to.resource/path',
{
ID: 12345
}
).then(
// Successful response received
function (response) {
console.log(response);
}
).catch(
// Error returned by the server
function (error) {
console.log(error);
}
);
Obviously in the catch handler you'd have your error handing code, either an alert or message appearing on the page. In the success you could have something along the lines of this.postList.push(new Post(response.data.name, response.data.link, response.data.uid, response.data.image));
To make it even easier you can assign axios to the vue prototype like this:
Vue.prototype.$http = axios
and make use of it using the local vm instance
this.$http.post("url", { data }).then(...);
EDIT:
For your multi-signature function edit it's best to use the arguments keyword. In Javascript the engine defines an arguments array containing the parameters passed to the function.
var Post = function Post(title, link, author, img) {
_classCallCheck(this, Post);
if(arguments.length == 1) {
this.title = title.title;
this.link = title.link;
this.author = title.author;
this.img = title.img;
} else {
this.title = title;
this.link = link;
this.author = author;
this.img = img;
}
};
Be careful not to mutate the arguments list as it's a reference list to the parameters themselves so you can overwrite your variables easily without knowing it.
Here is my working code (in Angular):
var url ="https://api.flickr.com/services/rest/?method=flickr.photos.search&
api_key=c4e2f731926eefa6fe1d3e9c2c9f9449&tags=coffee&format=json&jsoncallback=JSON_CALLBACK";
$http.jsonp(url).then(function(response) {
console.log(response.data);},function(){console.log('Error retrieving JSON data')}
);
However I do not know how to send totagsa variable's value and not just writing coffee or chocolate. Is there a better way to organize all this information (api_key, format, tags) in an object and append it to url?
You can use config.params object to set tags GET parameter:
var url = "https://api.flickr.com/services/rest/?api_key=c4e2f731926eefa6fe1d3e9c2c9f9449&method=flickr.photos.search&format=json&jsoncallback=JSON_CALLBACK";
$scope.tags = 'coffee';
$http.jsonp(url, {
params: { tags: $scope.tags }
})
.then(function(response) {
console.log(response.data);
}, function() {
console.log('Error retrieving JSON data')
});
Is there any way to get a base64 image (instead of png, jpg, pdf) from highcharts public export server?
server: http://export.highcharts.com/
Edit:
what I'm trying to do, is render the charts on server side and store them as base64. I'm able to do that by setting up a small web server following the instructions here highcharts.com/docs/export-module/render-charts-serverside but that means I need to host this in some place, and I'm trying to figure out if that's something I can avoid.
Since this is something I wanted to do from the backend and without the need of rendering the chart first, I ended up getting the image from the public export server and then convert it to base64 from the backend using RestSharp to do the request (C#)
public static string Render(Well well, string type)
{
var client = new RestClient("http://export.highcharts.com");
StringBuilder json = new StringBuilder('the options of the chart');
var request = new RestRequest("/", Method.POST);
request.AddHeader("Content-Type", "multipart/form-data");
request.AddParameter("content", "options");
request.AddParameter("options", json);
request.AddParameter("constr", "Chart");
request.AddParameter("type", "image/png");
var response = (RestResponse) client.Execute(request);
return Convert.ToBase64String(response.RawBytes);
}
I don't see the option base64 in the dropdown. So probably the answer is no.
But you could get the png, jpg or whatever and use something like base64 online to encode it.
Very much late to post But you can get base64 from http://export.highcharts.com.
You need to pass below configuration in Request
let chartData = {
infile: CHART_DATA,
b64: true // Bool, set to true to get base64 back instead of binary.
width: 600,
constr : "Chart"
}
You can use below example
fetch("https://export.highcharts.com/", {
"headers": {
"content-type": "application/json",
},
"body": "{\"infile\":\"{\\n \\\"xAxis\\\": {\\n \\\"categories\\\": [\\n \\\"Jan\\\",\\n \\\"Feb\\\",\\n \\\"Mar\\\",\\n \\\"Apr\\\",\\n \\\"May\\\",\\n \\\"Jun\\\",\\n \\\"Jul\\\",\\n \\\"Aug\\\",\\n \\\"Sep\\\",\\n \\\"Oct\\\",\\n \\\"Nov\\\",\\n \\\"Dec\\\"\\n ]\\n },\\n \\\"series\\\": [\\n {\\n \\\"data\\\": [1,3,2,4],\\n \\\"type\\\": \\\"line\\\"\\n },\\n {\\n \\\"data\\\": [5,3,4,2],\\n \\\"type\\\":\\\"line\\\"\\n }\\n ]\\n}\\n\",\"width\":600,\"constr\":\"Chart\",\"b64\":true}",
"method": "POST",
"mode": "cors"
}).then(function(response) {
// The response is a Response instance.
return response.text();
}).then(function(data) {
console.log(data); // base64 data
}).catch(function(err) { console.log(err);})