This question already has answers here:
How to download excel/Zip files in Angular 4
(2 answers)
Closed 5 years ago.
i'm trying to download excel file (.xls) using angular and php for backend
my backend already send my excel file as response, and when i check it, it return with correct format
but my angular 4 is returning the file as corrupted format (it contain some symbols like ��ࡱ�;�� )
below is my angular code:
Service
private headers = new Headers({
'Content-Type': 'application/x-www-form-urlencoded'
});
downloadTemplate() {
this.options = new RequestOptions();
this.options.headers = this.headers;
const params: URLSearchParams = new URLSearchParams();
params.set('token', this.currentUser.token);
this.options.search = params;
return this.http.get(environment.api_host + '/template', this.options);
}
Component
template() {
this._apiService.downloadTemplate().subscribe((response) => {
const blob = new Blob([(<any>response)._body], {type: 'application/vnd.ms-excel'});
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = 'template.xls';
document.body.appendChild(link);
link.click();
});
}
from my code, is there something that i miss?
If subscription is not necessary then just use this in downloadTemplate() method in your service
window.location.href = your_URL;
I'm using FileSaver library for serving files from Angular apps: http://alferov.github.io/angular-file-saver/
Here you have sample code:
getTemplate(): Observable<Blob> {
this.apiService.http()
.get(environment.api_host + '/template', { responseType: ResponseContentType.Blob })
.map(r => r.blob())
.subscribe(
template => {
FileSaver.saveAs(new Blob([template]), "template.xml");
},
error => {
console.error(error);
}
);
}
Important is that you should add header Content-Type: application/octet-stream to your PHP response. Complete and working set of headers:
$file = fopen("/tmp/examplefile.xls", "r");
array(
'Content-Disposition' => 'attachment; filename="' . basename($filePath) . '"',
'Content-Type' => 'application/octet-stream',
'Content-Length' => filesize($filePath),
'Expires' => '#0',
'Cache-Control' => 'must-revalidate',
'Pragma' => 'public'
);
I think the problem in your code is header definition: const blob = new Blob([(<any>response)._body], {type: 'application/vnd.ms-excel'}); or response type in http client options. You should send 'octet/stream' from PHP(binary data). In angular create Blob object from this data and return Blob object to user. That's all.
Related
So I have a josn object which has an array of objects which I want to send to a react native app through https but the problem is that I get null in react native
The code of the php :
<?php
class Product {
// Properties
public $title;
public $price;
}
header('Content-Type: application/json');
$ProductList =array();
$aa=$a->{'shopping_results'};
foreach($aa as $y => $y_value) {
$product = new Product();
$product->{'title'} = $y_value ->{'title'};
$product->{'price'} = $y_value ->{'price'};
array_push($ProductList,$product);
}
echo $x=json_encode(array('listx' => $ProductList),JSON_UNESCAPED_UNICODE);// the JSON_UNESCAPED_UNICODE for the Arabic letters
?>
When I try to view the content of this json on the browser this is what I get
https://i.stack.imgur.com/gXT4X.png
The react native code
await fetch(URL, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
// , body: JSON.stringify({ name: "tea" })
})
.then((response) => response.text()) //tried .json() got JSON Parse error: Unexpected EOF
.then((responseJson) => {
console.log(responseJson);//This prints blank
console.log("hi");
this.setState({ output: responseJson });//nothing shows
})
.catch((error) => {
console.error(error);
});
Note: I tried to receive a text from HTTPs request and it worked (The connection is fine)
You need to set HTTP headers, methods in your PHP code so as to accept requests from your react native app (basically I'm telling you to implement REST APIs). If already implemented, just make sure you are giving the correct endpoint in your react-native's fetch URL. And one more thing, when you are trying to retrieve data from the server make sure to set method: 'GET'.
If you're a beginner/ don't have prior knowledge about REST APIs, then here's a reference for you : https://www.positronx.io/create-simple-php-crud-rest-api-with-mysql-php-pdo/ I'm sure it'll give you some basic idea about your need.
I am trying to stream a large CSV from the server. I load a JSON file, convert it to an array, and then process it as a CSV. From the frontend, I am calling the following on a button click
downloadCSVData() {
axios({
url: '/downloadCSVData',
method: 'GET'
}).then((response) => {
console.log(response)
});
}
And then this function does the following
public function downloadCSVData()
{
//load JSON file
$data = file_get_contents($file_path, true);
//Convert file to array
$array = json_decode($data, true);
$headers = [
'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0'
, 'Content-type' => 'text/csv'
, 'Content-Disposition' => 'attachment; filename=galleries.csv'
, 'Expires' => '0'
, 'Pragma' => 'public'
];
$response = new StreamedResponse(function() use($array){
// Open output stream
$handle = fopen('php://output', 'w');
// Add CSV headers
fputcsv($handle, array_keys($array['element'][0]), ',');
foreach ($array['element'] as $key => $row) {
fputcsv($handle, $row);
}
// Close the output stream
fclose($handle);
}, 200, $headers);
return $response->send();
}
Now on the frontend, within the console, I can see the reponse being printed the way it needs to be. From my understanding however, the backend should trigger the file to be downloaded which is not happening.
Am I missing something here, how can I get this downloaded on the frontend as a physical file?
Thanks
If you visited the URL directly, your PHP settings should work to force a download. But you are already in a fully loaded HTML page, so you need to use the <iframe> trick to get the file to download. This will allow the browser to receive the incoming data as if you've opened a new browser window.
Try changing your download function to this:
downloadCSVData() {
$('<iframe />')
.attr('src', '/downloadCSVData')
.hide()
.appendTo('body');
}
This question already has answers here:
download file using an ajax request
(13 answers)
Closed 3 years ago.
I'm trying to export a CSV using the Laravel Excel Package https://docs.laravel-excel.com.
I need to pass a few variables in using an ajax request which is all working but the response isn't downloading the CSV.
I'm using the package in a few places without ajax just a simple export and it's all working so I know that side is all good.
Here is the ajax I'm using to send the data:
$(document).ready(function() {
$( "#export" ).on( "click", function() {
let clubInfo = $("#clubInfo select").val();
let funderInfo = $("#funderInfo select").val();
$.ajax({
headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
type: 'post',
url: '{{route('export')}}',
data: {club : clubInfo, funder: funderInfo},
}).done(function(response) {
console.log(response);
}).fail(function() {
console.log("Failed");
});
});
});
Here is the controller that handle the export:
public function adminExport(Request $request)
{
$club = $request->input('club');
$funder = $request->input('funder');
return Excel::download(new UsersExport($club, $funder), 'users.xlsx');
}
I'm not getting any PHP errors or warnings which is all good I'm just getting a response in the network tab which looks like it's the csv it's just not downloading.
The headers of the response are:
Accept-Ranges: none
Cache-Control: public
Connection: keep-alive
Content-Disposition: attachment; filename=users.xlsx
Content-Length: 6812
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Unless fetching the file through AJAX is a super hard requirement, I would suggest using window.open('your_url/your_route?club=XXXXX&funder=XXXXX', '_blank'); to download the file and avoiding any headaches.
I think you need to inform the type of export. Note the following:
return (new InvoicesExport)->download('invoices.csv', \Maatwebsite\Excel\Excel::CSV);
But in your controller, I can't see the export type:
return Excel::download(new UsersExport($club, $funder), 'users.xlsx');
If you are using version 2.1, you need to do this:
Excel::create('Filename', function($excel) {
//
})->export('csv');
Or ... what do you think about saving the export first and then exporting it like this?
$file_name = 'my_export';
// Find users first
$users = User::where([['club', $club], ['funder', $funder]])->get();
// then generate the file
Excel::create($file_name, function ($excel) use ($users) {
$excel->sheet('Users', function($sheet) use($users) {
$sheet->fromArray($users);
});
})->store('csv', storage_path('excel/export'));
// Finally, download the file
$full_path = storage_path('excel/export') . '/' . $file_name . '.csv';
return Storage::disk('public')->download($full_path, $file_name . 'csv',
['Content-Description' => 'File Transfer','Content-Type' => 'application/octet-
stream','Content-Disposition' => 'attachment; filename=' . $file_name .
'.csv']);
or maybe you would do that:
$headers = array(
'Content-Type' => 'text/csv',
);
return Response::download($file_name . 'csv', $full_path, $headers);
I'm trying to post form data, consists of text and image file to PHP server.
I use ionic native camera to get a picture from gallery:
const options: CameraOptions = {
quality: 70,
destinationType: this.camera.DestinationType.FILE_URI,
sourceType: this.camera.PictureSourceType.PHOTOLIBRARY,
saveToPhotoAlbum:false,
targetWidth: 400,
targetHeight: 400
}
this.camera.getPicture(options).then((imageData) => {
this.myphoto = normalizeURL(imageData);
this.myphoto2 = imageData;
}, (err) => {
});
And post it using form data:
var headers = new Headers();
headers.append('Content-Type', 'multipart/form-data;boundary=' + Math.random());
headers.append('Accept', 'application/json');
let options = new RequestOptions({
headers: headers
});
let formData = new FormData();
formData.append('judul', this.judul.value);
formData.append('photo', this.myphoto, '123.jpg');
this.http.post('http://localhost/upload.php', formData, options)
.map(...)
.subscribe(...);
But, I saw on PHP log, the form-data not sent by ionic.
What's wrong here?
You can try removing 'options' from the this.http.post() arguments.
Your code becomes :
this.http.post('http://localhost/upload.php', formData)
.map(...)
.subscribe(...);
If this doesn't work,
try sending across the entire BASE64 Image using
'destinationType: this.camera.DestinationType.DATA_URL'
i advise you use the native plugin to upload file :
https://ionicframework.com/docs/native/file-transfer/
First Take Photo From Camera
async takePhoto() {
const options: CameraOptions = {
quality: 70,
destinationType: this.camera.DestinationType.FILE_URI,
sourceType: this.camera.PictureSourceType.PHOTOLIBRARY,
saveToPhotoAlbum:false,
targetWidth: 400,
targetHeight: 400
}
this.camera.getPicture(options).then((imageData) => {
this.getSystemURL(imageData);
}, (err) => {
});
}
Than Get a Local System Image URL
private getSystemURL(imageFileUri: any): void {
this.file.resolveLocalFilesystemUrl(imageFileUri)
.then(entry => (entry as FileEntry).file(file => {
this.readFile(file);
}))
.catch(err => console.log(err));
}
After Read That Image URL
private readFile(file: any) {
const reader = new FileReader();
reader.onloadend = () => {
this.myphoto = new Blob([reader.result], {type: file.type});
var headers = new Headers();
headers.append('Content-Type', 'multipart/form-data;boundary=' + Math.random());
headers.append('Accept', 'application/json');
let options = new RequestOptions({
headers: headers
});
let formData = new FormData();
formData.append('judul', this.judul.value);
formData.append('photo', this.myphoto, '123.jpg');
this.http.post('http://localhost/upload.php', formData, options)
.map(...)
.subscribe(...);
};
reader.readAsArrayBuffer(file);
}
It is working for me,
Hope it will help.
tl;dr:
Using Angular 6 on the front end and PHP with Phalcon on the backend, I can send JSON data or a File with no problem but I am having a problem sending both in the same request.
Previously I was sending JSON data to the server using something like this
const HTTP_OPTIONS = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
}),
observe: 'response'
};
post(endPoint: string, body: object): Observable<any> {
return this.http.post<any>(this.apiUrl + endPoint, body, HTTP_OPTIONS)
.pipe(
tap(result => this.log(JSON.stringify(result, null, 2))),
catchError(this.handleError('post', []))
);
}
And I was able to get the data from PHP using Phalcon with
$app = new \Phalcon\Mvc\Micro();
$app->post('/upload', function() use ($app) {
$input = $app->request->getJsonRawBody();
// $input now contains my JSON data
});
Some time later, I needed to send a file so I used this answer with some minor modifications:
postFile(fileToUpload: File, endpoint: string): Observable<any> {
const formData: FormData = new FormData();
formData.append('fileKey', fileToUpload, fileToUpload.name);
return this.httpClient
.post(endpoint, formData, { headers: {'Authorization': this.jwt} }).pipe(
tap(result => this.log(JSON.stringify(result, null, 2))),
catchError(this.handleError('post', []))
);
}
And I received my file with no problems using the documentation as a guide:
$app->post('/uploads', function() use ($app) {
if ($app->request->hasFiles() == true) {
foreach ($app->request->getUploadedFiles() as $file) {
$file->moveTo('files/' .$file->getname());
}
} else {
$app->response->setStatusCode(400)->sendHeaders();
$app->response->setJsonContent(['error' => 'no file']);
return $app->response;
}
});
The problem: Now I would like to send both a file and some JSON data at the same time. I can always just upload the file and then send the data separately but I don't think that's the right way to do it. I don't want to make more than the minimum number of network calls.
What I've tried: Using the file upload code and simply appending another field to my FormData object with my JSON data
formData.append('fileKey', fileToUpload, fileToUpload.name);
formData.append('data', JSON.stringify(data));
and a variation of that
formData.append('fileKey', fileToUpload, fileToUpload.name);
formData.append('data', new Blob([JSON.stringify(data), {type: 'application/json'}]);
Either way, on the backend I can get the file but $app->request->getJsonRawBody and $app->request->getRawBody are empty.
I also tried using the original JSON-sending code and just changing a bit to include the file but with no success.
post(fileToUpload: File, data: CustomData): Observable<any> {
this.messageService.add('uploaded file');
const formData: FormData = new FormData();
formData.append('fileKey', fileToUpload, fileToUpload.name);
formData.append('data', JSON.stringify(data), 'data');
return this.http
.post(this.apiUrl + 'uploads/', {'data': data, 'file': fileToUpload}, HTTP_OPTIONS).pipe( // file is empty on the server like this
tap(result => this.log('POST file :\n' + JSON.stringify(result, null, 2))),
catchError(this.handleError('post', [], 'fileUpload'))
);
}
I can easily send either my JSON data or the file but not both.
I searched the Phalcon documentation and several QAs on sending files and/or JSON with Angular but I cannot figure out how to make this work.
You are sending json as text in post request, so instead of $app->request->getJsonRawBody you should try something like
$rawJson=$app->request->getPost('data');
$object=json_decode($rawJson);
you can get your json as #Błażej Kowalczyk said
$this->request->getPost()
and you can check for files and get them
if ($this->request->hasFiles()) {
foreach ($this->request->getUploadedFiles() as $file) {
// $file is an instance of Phalcon\Http\Request\File
var_dump($file->getName());
}
}
check these pages for more information
https://docs.phalconphp.com/3.4/en/api/phalcon_http_request
https://docs.phalconphp.com/3.4/en/api/phalcon_http_request_file