Downloading Excel data using Axios from Laravel backend is not working - excel

I am developing a Web application using React JS for the front-end and Laravel for the back-end API. Now, what I am trying to do is I am trying to fetch the Excel data from the backend using Axios and then download the file.
This is my Laravel API controller action method.
function downloadExcel(Request $request)
{
//other code goes here
return Excel::create($left_photo->id . "-" . $right_photo->id, function($excel) use ($excel_data)
{
// Set the spreadsheet title, creator, and description
$excel->setTitle('Mapping points');
$excel->setCreator('Laravel')->setCompany('Memento');
$excel->setDescription('Mapping points file');
// Build the spreadsheet, passing in the payments array
$excel->sheet('sheet1', function($sheet) use ($excel_data)
{
$sheet->fromArray($excel_data, null, 'A1', false, false);
});
})->download('xlsx');
}
I fetch the data from the react js application using Axios like this.
export const getHttpClientFileDownload = (path) => {
let accessToken = localStorage.getItem("access_token");
return Axios({
url: getApiBaseEndpoint() + path,
method: 'GET',
responseType: 'blob', // important
headers : { 'api-version': API_VERSION, 'Authorization': 'Bearer ' + accessToken }
})
}
exportExcel()//this is the download medthod in the component
{
let path = 'photos/matching-points/excel?left_photo_id=' + this.props.leftImageId + "&right_photo_id=" + this.props.rightImageId;
//let path = "curator/event/" +this.props.match.params.id + "/details";
getHttpClientFileDownload(path)
.then((response) => {
alert('Everything is alright')
})
}
As you can see in the above code, if the request success, it should alert a message, "Everything is alright". But it is not alerting the message. But in the browser, it is successful.
When I make the request to the link that is returning just normal JSON response, it is alerting the message as expected. Only it is not working as expected when I make the request to the aforementioned Excel API.
I cannot use direct download link because I am doing some authorization on the server-side.

I had the same problem and found a solution as below.
Steps:
Call API to Laravel backend. Create file and store the same in local driver on server.
Return the file name to client.
Create a public route in Laravel (In web.php) to download files from Local storage. This route will delete the file after download it.
From client side, redirect the user to this public URL with the file name.
My code looks like this. I used fetch API.
Client side code:
const response = await fetch("my_server_url.com/api/createFile", {
method: 'GET',
headers: {
Authorization: "Bearer " + this.$store.state.AccessToken,
"X-Requested-With": "XMLHttpRequest",
},
});
if (!response.ok) {
console.log(response)
throw new Error("Something went wrong!");
}
const data = await response.json();
window.open("my_server_url.com/downloadFile/name="+data, '_blank');
Code in my_server_url.com/api/createFile route: (api.php)
public function createFile()
{
$file_name= date('YmdHis').rand();
Excel::store(new myExport(), $file_name.'.xlsx', 'local');
return response()->json($file_name.'.xlsx', 200);
}
Code in my_server_url.com/downloadFile/name={file_name} route: (web.php)
public function downloadFile($file_name)
{
return response()->download(Storage::path($file_name))->deleteFileAfterSend(true);
}
This way, you can check authorization and logic, but yet using the API. Also, make sure to add use Illuminate\Support\Facades\Storage; in laravel controller.

You can simply use window.open(path) to download files

Related

How can I use Axios to access the JSON data within a response sent using Expressjs?

I'm creating a web application that generates a pdf on a server then sends it to the client for display within the browser.
The client is using Vuejs / Axios to send a POST request. Afterwards, The server is receiving it with Expressjs, generating a unique PDF, converting the file to a base64 value then sending it back as a response.
I cannot seem to get the response correct. When I attempt to display response.data.pdfData within the client I get undefined in the console. I can see that there is indeed a response with the key and value pair using inspection tools within the Network tab under the Preview section but cannot seem to access it.
// FILE: ./Client/src/App.vue
submit(personalInfo) {
this.cardInfo.personalInfo = personalInfo;
console.log('Sending POST preview_card...');
axios({
url: 'http://localhost:5000/api/preview_card',
method: 'POST',
responseType: 'blob',
data: {
cardInfo: this.cardInfo,
},
}).then((response) => {
console.log(response.data.pdfData);
});
},
// FILE: ./Server/app.js
app.post('/api/preview_card', (req, res) => {
// Generate pdf
const doc = new jsPDF('p');
doc.setFontSize(40);
doc.text(req.body.cardInfo.templateInfo, 100, 100);
doc.save('response.pdf');
// Convert pdf to base64
var tempFile = path.resolve(__dirname, './response.pdf');
var pdfBase64 = fs.readFileSync(tempFile).toString('base64');
res.setHeader('Content-Type', 'application/json');
return res.send(JSON.stringify({ pdfData: pdfBase64 }));
});
I find it necessary to serve the pdf this way due to my client's compnents as well as to enforce a level of data coherency between concurrent users.

Empty response in API from Dio.Post method in Flutter

I have been trying to connect to a local Node JS API using Flutter Web. I am using the Dio dart Package with a GET and a POST method, however, the response of my POST method does not return anything. See below a few instances of code and what I am seeing:
On my Node JS app I am receiving information from flutter:
'Server is listening on port 8000'
Successfully connected
/login2
{}
The /login2 path and the {} are a console.log in my API from the Flutter incoming data. See now the Dio.post method:
Future post(String path, Map<String, dynamic> data) async {
try {
final result = await _dio.post(path, data: data);
return result.data;
} catch (e) {
print(e);
throw ('Error en el POST');
}
}
Also tried with HTTP, heres the piece of code:
Future post2(String id, String password) async {
print(id);
print(password);
final response = await http.post(
Uri.parse('http://localhost:8000/login2'),
headers: {
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode({'user': id, 'password': password}),
);
print(response);
}
I have tried with Postman and this is what I get:
/login2?user=10002&password=123456
{ user: '10002', password: '123456' }
That is both the path and the query {} from the same console.log that is empty from Flutter, meaning that my Node server is indeed receiving data.
Trying as per #Wali Kan suggestion with the Postman configuration:
Future post(String path, Map<String, dynamic> data) async {
final String formData = data.toString();
var headers = {'Content-Type': 'text/plain'};
var request =
http.Request('POST', Uri.parse('http://localhost:8000' + path));
request.body = formData;
print(request.body);
http.StreamedResponse response = await request.send();
print(response);
request.headers.addAll(headers);
if (response.statusCode == 200) {
print(await response.stream.bytesToString());
} else {
print(response.reasonPhrase);
}
Still receiving /login2 and {} on the API side.
What I am missing is how to succesfully receive Flutter data so that I can complete the login process on my Node API and then redirect to the user profile page.
Any recommendations on what to test or check? If you need me to share any more pieces of the code let me know, been stuck here for many hours now... Thank you for your time.
headers
HttpHeaders.acceptHeader: "json/application/json",
HttpHeaders.contentTypeHeader: "application/x-www-form-urlencoded"
make sure not to use FormData.fromMap({}) just send your data as normal object and don't forget to include the application/x-www-form-urlencoded content type in your headers.
in case your server is running express make sure you have these middleware before processing the request
app.use(express.json())
app.use(express.urlencoded({ extended: true }));

Walmart Seller API "Bulk Item Setup" doesn't work

I tried to use Walmart API v4.2 to publish some items. I used "Bulk Item Setup" API method to create some feed. I used some types of ways to did it:
Send binary file (in the request body, for-data) with header "multipart/form-data" (this way was described in the Walmart API docs)
Send stringified object in the request body with header 'Content-Type': 'application/json',
Walmart API correctly returns me feedId.
But all of these ways didn't work! After feed creating, I saw "Submitted" status at the Walmart Seller Center. But this status was changed after few minutes to "Error". At the error column I see "ERROR TYPE: Data Error" with a description "Malformed data. Please check data file to ensure it is formatted properly.java.lang.NullPointerException".
I use my backend NodeJs app to do it. I use Axios for making a request.
My code example:
async createFeed(wdpId, wdpSecret, accessToken, feedsData) {
try {
const string = JSON.stringify(feedsData);
const file = Buffer.from(string);
const formData = new FormData();
formData.append('file', file);
const baseToken = WalmartService.getBaseAuthToken(wdpId, wdpSecret);
const options = {
params: {
feedType: 'MP_WFS_ITEM',
},
headers: {
Authorization: baseToken,
'WM_SEC.ACCESS_TOKEN': accessToken,
'WM_QOS.CORRELATION_ID': uuidv4(),
'WM_SVC.NAME': 'Walmart Marketplace',
Accept: 'application/json',
'Content-Type': 'application/json',
...formData.getHeaders(),
},
};
return (
axios
.post(`${process.env.WALMART_API_BASEURL}/feeds`, formData, options)
.then((response) => {
return response.data;
})
.catch((error) => {
console.error(error.message);
throw new BadRequestException('Walmart error, ', error.message);
})
);
} catch (error) {
throw new BadRequestException('Can not create listing');
}
}
It is difficult to identify the exact issue based on the information you provided. Few things that you might want to check
If you are appending/attaching a file (as I see it in the code), use Content-Type header as "multipart/form-data. Also, make sure the file name has a .json extension if you are sending data as a json string. If you don't use this, it might default to xml and you will get the same error as what you see.
Try invoking the API using a rest client like Postman and verify if that call is successful.
If you do want to send the data as HTTP body (instead of a file), that should work too with Content-Type as application/json. This has not been documented on their developer portal, but it works.

Image upload in Nuxt.js using axios

I've tried almost every answer in relative questions, and couldn't find any solution to my case.
I'm new to Nuxt.JS, and I'm moving my project from Vue/CLI to Nuxt.js, now I'm stuck in sending POST request which contains images and data (FormData).
The FormData is appearing empty on the server side (Node.js)
The current working version of my code in Vue CLI:
const requestOptions = {
method: 'POST',
body: formData
};
return fetch(`/create`, requestOptions).then(handleResponse);
What I'm trying to achieve in Nuxt.JS (which is not working properly) by using nuxt/axios module:
methods: {
async sendRequest(){
let formData = new FormData();
formData.append('image',this.myFile);
formData.append('name',this.anyName);
var res = await this.$axios.$post('/create', formData);
}
}
EDIT: I tried to log the content before making the request like:
for (var pair of formData.entries()) {
console.log(pair[0]+ ' - ' + pair[1]);
}
And I can see the fields and values clearly as intended.
I've tried adding headers to the request:
this.$axios.$post('/create', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
But still getting: {} in Node.js while printing the request body.
What I'm doing wrong?

Get image from Axios and send as Form Data to Wordpress API in a Cloud Function

What I'm trying to accomplish is using a Firebase Cloud Function (Node.js) to:
First download an image from an url (f.eg. from unsplash.com) using an axios.get() request
Secondly take that image and upload it to a Wordpress site using the Wordpress Rest API
The problem seems (to me) to be that the formData doesnt actually append any data, but the axios.get() request actually does indeed retrieve a buffered image it seems. Maybe its something wrong I'm doing with the Node.js library form-data or maybe I get the image in the wrong encoding? This is my best (but unsuccessfull) attempt:
async function uploadMediaToWordpress() {
var FormData = require("form-data");
var formData = new FormData();
var response = await axios.get(
"https://images.unsplash.com/photo-1610303785445-41db41838e3e?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80"
{ responseType: "arraybuffer" }
);
formData.append("file", response.data);
try {
var uploadedMedia = await axios.post("https://wordpresssite.com/wp-json/wp/v2/media",
formData, {
headers: {
"Content-Disposition": 'form-data; filename="example.jpeg"',
"Content-Type": "image/jpeg",
Authorization: "Bearer <jwt_token>",
},
});
} catch (error) {
console.log(error);
throw new functions.https.HttpsError("failed-precondition", "WP media upload failed");
}
return uploadedMedia.data;
}
I have previously successfully uploaded an image to Wordpress with Javascript in a browser like this:
async function uploadMediaToWordpress() {
let formData = new FormData();
const response = await fetch("https://images.unsplash.com/photo-1610303785445-41db41838e3e?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80");
const blob = await response.blob();
const file = new File([blob], "image.jpeg", { type: blob.type });
formData.append("file", file);
var uploadedMedia = await axios.post("https://wordpresssite.com/wp-json/wp/v2/media",
formData, {
headers: {
"Content-Disposition": 'form-data; filename="example.jpeg"',
"Content-Type": "image/jpeg",
Authorization: "Bearer <jwt_token>",
},
});
return uploadedMedia.data;
},
I have tried the last couple of days to get this to work but cannot for the life of me seem to get it right. Any pointer in the right direction would be greatly appreciated!
The "regular" JavaScript code (used in a browser) works because the image is sent as a file (see the new File in your code), but your Node.js code is not really doing that, e.g. the Content-Type value is wrong which should be multipart/form-data; boundary=----...... Nonetheless, instead of trying (hard) with the arraybuffer response, I suggest you to use stream just as in the axios documentation and form-data documentation.
So in your case, you'd want to:
Set stream as the responseType:
axios.get(
'https://images.unsplash.com/photo-1610303785445-41db41838e3e?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80',
{ responseType: 'stream' }
)
Use formData.getHeaders() in the headers of your file upload request (to the /wp/v2/media endpoint):
axios.post( 'https://wordpresssite.com/wp-json/wp/v2/media', formData, {
headers: {
...formData.getHeaders(),
Authorization: 'Bearer ...'
},
} )
And because the remote image from Unsplash.com does not use a static name (e.g. image-name.jpg), then you'll need to set the name when you call formData.append():
formData.append( 'file', response.data, 'your-custom-image-name.jpeg' );
I hope that helps, which worked fine for me (using the node command for Node.js version 14.15.4, the latest release as of writing).

Resources