Actually my task is when I select from-date and to-date using date picker and then I click download button, select the id from orders table between dates and check invoice->id is matching with files in storage folder. If files are matched create zip and download.
I can zipped but download is not working.
export.blade.php
<button class="btn btn-success dateDifference" role="button"><i class="fa fa-download"></i> Download </button>
<script>
$(".dateDifference").on("click", function() {
var fromDate = $(".dateFrom").val();
var toDate = $(".dateTo").val();
$.post('{{ route('exportInvoiceDownload') }}', {fromDate: fromDate, toDate: toDate}, function(response){
//alert(response);
});
});
</script>
routes.php
Route::post('/export/download/invoices', [
'as' => 'exportInvoiceDownload', 'uses' => 'BookingController#downloadInvoices'
]);
Controller.php
public function downloadInvoices(Request $request)
{
$fromDateReplace = str_replace('/', '-', $request->fromDate);
$fromDate = date("Y-m-d", strtotime($fromDateReplace));
$toDateReplace = str_replace('/', '-', $request->toDate);
$toDate = date("Y-m-d", strtotime($toDateReplace));
$archive_name = storage_path('app') . '/invoice_archive/Rechnung_'.$fromDate.'_'.$toDate.'.zip';
$orders = Booking::select('invoice_id')
->whereBetween('created_at', [$fromDate, $toDate])
->get();
if (count($orders) > 0 ) {
$zip = new ZipArchive;
if ($zip->open($archive_name, ZipArchive::CREATE) === TRUE) {
foreach ($orders as $order) {
$file = storage_path('app') . '/invoice_archive/test-'.$order->invoice_id.'.pdf';
if (file_exists($file)) {
$zip->addFile($file, 'test-'.$order->invoice_id.'.pdf');
}
}
$zip->close();
$headers = [
'Pragma' => 'public',
'Expires' => 0,
'Content-Type' => 'application/octet-stream',
'Content-Disposition' => 'attachment; filename=\"test_'.$fromDate.'_'.$toDate.'.zip\"',
'Content-Transfer-Encoding' => 'binary'
];
if (file_exists($archive_name)) {
return response()->download($archive_name, 'test_'.$fromDate.'_'.$toDate.'.zip', $headers) /*->deleteFileAfterSend(TRUE)*/;
}
}
}
}
seems you are calling this route with ajax, if that's true than download won't work in this case, you need traditional post request because ajax binary response will not open download popup in browser so instead of ajax just submit the form as browser default or make it get request and open it in new browser tab then it will consider content-dispostion header and download popup will appear.
Related
Currently I am setting up a queue to insert data from CSV files to the DB using file upload. The queue and data extraction is working currently, but I am looking for a way to check whether the job has been completed, failed or still in process. Is it possible to achieve this and update the status of uploaded file in other DB table as well?
Jobs
Redis::throttle('upload-csv')->allow(1)->every(20)->then(function () {
dump('Processing this file:---',$this->file);
$data = array_map('str_getcsv',file($this->file));
foreach($data as $row){
CSVDataList::updateOrCreate([
'KEY_1' => $row[0],
],[
'KEY_2' => $row[1],
'KEY_3' => $row[2],
'KEY_4' => $row[3],
]);
}
dump('Done for this file:---',$this->file);
unlink($this->file);
}, function () {
return $this->release(10);
});
Controller
$request->validate([
'file' => 'required|mimes:csv,txt'
]);
$filename = $file->getClientOriginalName();
$file = file($request->file->getRealPath());
$data = array_slice($file,1);
$parts = (array_chunk($data,5000));
foreach($parts as $index=>$part){
$fileName = resource_path('pending-files/'.date('y-m-d-H-i-s').$index.'.csv');
file_put_contents($fileName,$part);
}
(new CSVDataList())->importToDb($filename);
session()->flash('status','Queued for importing..');
return redirect('file-upload');
Function in Model
public function importToDb($filename)
{
$path = resource_path('pending-files/*.csv');
$files = glob($path);
foreach($files as $file){
ProcessCsvUpload::dispatch($filename,$file);
}
}
Found a handful of questions on here about this with no answer, so hopefully, someone can point me in the right direction...
I'm trying to create and save a csv file to storage, then update the DB in Laravel. I can create the file successfully, and I can update the DB successfully... but I'm stuck on putting them both together. In my controller, I have this for creating the file (taken from here):
public function updatePaymentConfirm(Request $request) {
$users = User::all();
$fileName = 'test.csv';
$headers = array(
"Content-type" => "text/csv",
"Content-Disposition" => "attachment; filename=$fileName",
"Pragma" => "no-cache",
"Cache-Control" => "must-revalidate, post-check=0, pre-check=0",
"Expires" => "0"
);
$columns = array('First Name', 'Email');
$callback = function() use($users, $columns) {
$file = fopen('php://output', 'w');
fputcsv($file, $columns);
foreach ($users as $user) {
$row['First Name'] = $user->first_name;
$row['Email'] = $user->email;
fputcsv($file, array($row['First Name'], $row['Email']));
}
fclose($file);
};
// return response()->stream($callback, 200, $headers);
}
When the function completes, the last line (that's commented out) prompts the user to download the newly created file (which is not the functionality I'm looking for). I tried adding this to my controller in its place for saving to storage and also updating the database:
$fileModel = new UserDocument;
if($callback) {
$filePath = $callback->storeAs('uploads', $fileName, 'public');
$fileModel->name = $fileName;
$fileModel->file_path = '/storage/' . $filePath;
$fileModel->save();
return back()
->with('success','File has been uploaded.')
->with('file', $fileName);
}
It saves a row to the db, albeit incorrectly, but it doesn't save the file to storage. I've reworked the $filePath line a million times, but I keep getting this error Call to a member function storeAs() on resource or something similar. I'm relatively new to working with Laravel, so I'm not sure what I should be looking for. Thoughts?
Removed everything and started over... got it! And for anyone else running into the same issue: just calling for a file that doesn't exist creates the file (unless the file exists - then it updates it), so you don't have to create a temp file or use $file = fopen('php://output', 'w'); to create the file. It'll automatically "save" the newly generated file in the file path you specified when you fclose() out of the file.
The only thing I'll note is that the file path has to exist (the file doesn't, but the file path does). In my instance, the file path already exists, but if yours doesn't or if you're not sure if it does, check to see if it exists, and then make the directory.
public function updatePaymentConfirm(Request $request) {
$user = Auth::user();
$path = storage_path('app/public/docs/user_docs/'.$user->id);
$fileName = $user->ein.'.csv';
$file = fopen($path.$fileName, 'w');
$columns = array('First Name', 'Email Address');
fputcsv($file, $columns);
$data = [
'First Name' => $user->first_name,
'Email Address' => $user->email,
];
fputcsv($file, $data);
fclose($file);
$symlink = 'public/docs/user_docs/'.$user->id.'/';
$fileModel = new UserDocument;
$fileModel->name = 'csv';
$fileModel->file_path = $symlink.$fileName;
$fileModel->save();
return redirect()->route('completed');
}
** UPDATE **
Everything worked perfectly locally, and when I pushed this to production, I received this error 🙄:
fopen(https://..../12-3456789.csv): failed to open stream: HTTP wrapper does not support writeable connections.
I'm saving to an s3 bucket, and I had to rework the entire process. You can't create and/or write to a file in the directory. I had to create a temp file first. Here's where I landed:
$user = Auth::user();
$s3 = Storage::disk('s3');
$storage = Storage::disk('s3')->url('/');
$path = 'public/docs/user_docs/'.$user->id.'/';
$csvFile = tmpfile();
$csvPath = stream_get_meta_data($csvFile)['uri'];
$fd = fopen($csvPath, 'w');
$columns = array('First Name', 'Email Address');
$data = array(
'First Name' => $user->first_name,
'Email Address' => $user->email,
);
fputcsv($fd, $columns);
fputcsv($fd, $data);
fclose($fd);
$s3->putFileAs('', $csvPath, $path.$user->ein.'.csv');
Today I have fixed it with this snipe:
// output up to 5MB is kept in memory, if it becomes bigger it will
// automatically be written to a temporary file
$csv = fopen('php://temp/maxmemory:'. (5*1024*1024), 'r+');
fputcsv($csv, array('blah','blah'));
rewind($csv);
$output = stream_get_contents($csv);
// Put the content directly in file into the disk
Storage::disk('myDisk')->put("report.csv", $output);
This code is easy and functional, use Laravel Storage Class
https://laravel.com/docs/9.x/filesystem#main-content
use Illuminate\Support\Facades\Storage;
// data array
$results = [
['id' => 0, 'name' => 'David', 'parent' => 1],
['id' => 1, 'name' => 'Ron', 'parent' => 0],
['id' => 2, 'name' => 'Mark', 'parent' => 1]
];
// create a variable to store data
$pages = "id,name,parent\n"; // use " not ' or \n not working
// use foreach to data
foreach ($results as $where) {
$pages .= "{$where['id']},{$where['name']},{$where['parent']}\n";
}
// use Fecades Laravel Storage
Storage::disk('local')->put('file.csv', $pages);
[SOLVED] Had to split the download() method in 2, so it goes as generateCSV() and then getDownload().
After the the generation of the file, added this on sweetalert confirm button, which route points to getDownload().
preConfirm: () => {
window.location.href = "/customers/resale/filterToCSV/download";
}
After the user selects a few checkboxes for filtering a database table, the server writes to a CSV file, but it doesn't prompt the browser to download it.
route:
Route::get('/customers/resale/filterToCSV', 'Resale_customerController#getFilteredQueryResults');
blade view:
axios.get('/customers/resale/filterToCSV', {
params: {
dataFromClient: arrJson,
}
})
.then(function (response) {
Swal.fire({
icon: 'success',
title: '...',
text: '...',
})
console.log("Response (Filtered data to CSV): " + response.data);
});
controller:
public function getFilteredQueryResults(Request $request)
{
$arr = json_decode($request->dataFromClient, true);
$selection = $this->queryBuilderFromCheckboxSelection($arr);
$jsn = $selection->toJson();
$this->download($jsn);
}
which calls the download() method:
public function download($jsn)
{
$filePath = public_path().'\\file.csv';
$headers = array(
"Content-type" => "text/csv",
"Content-Disposition" => "attachment; filename=file.csv",
"Pragma" => "no-cache",
"Cache-Control" => "must-revalidate, post-check=0, pre-check=0",
"Expires" => "0"
);
$jsonDecoded = json_decode($jsn, true);
$csvFileName = 'file.csv';
$fp = fopen($csvFileName, 'w');
foreach ($jsonDecoded as $row) {
fputcsv($fp, $row);
}
fclose($fp);
echo response()->download($filePath, $csvFileName, $headers);
return response()->download($filePath, $csvFileName, $headers);//->deleteFileAfterSend(true);
}
Any idea what am I missing? Thank you!
Below is some background on why I am asking this question followed by some research I have done as of now before bringing this question forward. I hope that I am asking the correct question. As is it often the case that we ask the wrong question because we do not fully understand the issue and therefore waste our time and those of others.
Discovery of an issue:
I click a button on my website that is supposed to save edits to an article. When I do click the button, I get the following : it turns white and nothing changes.
Below is the code for the button in question this button exists in the file community/profile.php.
<button onclick="saveEditedArticle();" class="btn btn-dark btn-lg btn-block" id="edit_postbutton">Save Article</button>
Initial Steps Taken To Investigate The Problem
I opened up the chrome dev tools to see what could possibly be happening.
1) In Chrome Dev Tools I Get A 403 Error
10jquery.js:8630 POST https://bullbuster.net/community/controllers/article.php 403
send # jquery.js:8630
ajax # jquery.js:8166
saveEditedArticle # (index):591
onclick # (index):6451
2) I Do Some Research To See What A 403 Error Is
3) I Use Google & Find Stack Overflow Questions On This Issue:
403 forbidden error -executing and ajax page
403 Forbidden error when making an ajax Post request
4) I am told a 403 Error Means That My File Has The Wrong Permissions
I then change the permissions for :
https://bullbuster.net/community/controllers/article.php
to 0755 (what this means) from 0644 (what this means)
5) I am told it is possible that it is an issue with my htaccess file:
Header set Access-Control-Allow-Origin "*"
Below is the part of the file from the community/controllers/article.php that access is being forbidden from (403) even though this file has 0755 permissions
case 'save-edit':
//echo 'made it';
$validation = $validate->check($_POST, array(
'edit_article_category' => array(
'required' => true,
),
'edit_new_post_title' => array(
'required' => true,
),
'edit_new_post_content' => array(
'required' => true,
),
'edit_line_type' => array(
'required' => true,
), /*
'edit_lbtest-select' => array(
'required' => true,
),*/
));
if($validation->passed()){
$update = $Ambassador->saveEditedArticle();
The function saveEditedArticle is found in two files:
1) community/profile.php
function saveEditedArticle()
{
event.preventDefault();
$("#edit_formatted-tags").val($("#edit_tags").tagsinput('items'));
//alert(tags);
//var form = $("#article").serialize();
//alert(tags);
$('.selectpicker').selectpicker('setStyle', 'error-input', 'remove');
$('#new_post_title').removeClass('error-input');
$('#redactor-uuid-0').removeClass("error-input");
$.ajax({
url: './controllers/article.php',
type: 'post',
dataType: 'json',
crossDomain: 'true',
data:
$("#edit_article").serialize()
,
success: function(data){
if(data == 'validation passed'){
$('#edit_validation-error').hide();
$('#edit_postbutton').attr('disabled', 'true');
$('#edit_submission-success').show();
setTimeout(function() {
//location.reload();
var location = window.location.href;
window.location = location;
}, 1500);
} else {
for (i = 0; i < data.length; i++) {
var field = data[i][0];
//$('#'+field).addClass("error-input");
if(field=='edit_new_post_title'){
$('#edit_new_post_title').addClass("error-input");
}
if(field=='edit_new_post_content'){
$('.redactor-editor').addClass("error-input");
}
$('#'+field).selectpicker('setStyle', 'error-input');
$('#edit_validation-error').show();
}
}
}
});
}
2) This is in the file classes/ambassador.php
public function saveEditedArticle(){
$content = Input::get('edit_new_post_content');
$start = "<img src=\"";
$end = "\"";
$image = self::getBetween($content, $start, $end);
if($image==''){
$start = "src=\"//www.youtube.com/embed/";
$end = "\"";
$image = self::getBetween($content, $start, $end);
if($image!=''){
$image = "https://img.youtube.com/vi/" . $image . "/0.jpg";
}
if($image==''){
$start = "//player.vimeo.com/video/";
$end = "\"";
$image = self::getBetween($content, $start, $end);
if($image!=''){
$link = 'https://vimeo.com/api/v2/video/' . $image . '.php';
$html_returned = unserialize(file_get_contents($link));
$image = $html_returned[0]['thumbnail_large'];
}
}
}
if($image==''){
$image = 'img/articles/475-33d0f7b0970af583c001559f19c26911.jpg';
}
//$content = str_replace('http://', 'https://', Input::get('new_post_content'));
$slug1 = strtolower(Input::get('edit_article_category'));
$slug2 = strtolower(Input::get('edit_new_post_title'));
$slug2 = preg_replace('/[^a-zA-Z0-9 ]/s', '', $slug2);
$slug = $slug1 . '/' . $slug2;
$slug = str_replace(' ', '-', $slug);
$slug = preg_replace ('/-+/', '-', $slug);
$article_category = Input::get('edit_article_category');
$article_title = ucwords(Input::get('edit_new_post_title'));
$article_content = Input::get('edit_new_post_content');
$line_category_id = Input::get('edit_line_type');
//$lb_test_id = Input::get('edit_lbtest-select');
$tags = Input::get('edit_formatted-tags');
$article_id = Input::get('article_id');
$update = $this->_db->update('Article', $article_id,
array(
'category'=>$article_category,
'title'=>$article_title,
'content'=>$article_content,
'line_category_id'=>$line_category_id,
//'lb_test_id'=>$lb_test_id,
'tags'=>$tags,
'image_preview'=>$image,
'slug'=>$slug
),
'id');
}
I found this useful tool called RubaXa / jquery.fileapi
which slices a file and create blobs. But within the documentation there is a lack of information about how to process the uploaded parts on the server. The example on the page refers to a url: './ctrl.php' but you can't see the content of it on the developers page. I used this (look below) client side script so far and there were no errors. A file was sliced and several post requests appeared in my firebug console. So it seems to work. But how to process the received fileparts in PHP on the server?
imported scripts:
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.min.js"></script>
<script>
window.FileAPI = {
debug: false // debug mode
, staticPath: 'jquery.fileapi-master/FileAPI/' // path to *.swf
};
</script>
<script src="jquery.fileapi-master/FileAPI/FileAPI.min.js"></script>
<script src="jquery.fileapi-master/jquery.fileapi.min.js"></script>
the initiation and setup of the fileuploader:
jQuery(function ($){
$('#uploader').fileapi({
url: 'stash.php',
autoUpload: true,
accept: 'video/*',
multiple: false, //only single file upload
chunkSize: .5 * FileAPI.MB //filesize of the blobs/chunks
});
});
the HTML file upload "form":
<div id="uploader">
<div class="js-fileapi-wrapper">
<input type="file" name="files[]" />
</div>
<div data-fileapi="active.show" class="progress">
<div data-fileapi="progress" class="progress__bar"></div>
</div>
</div>
Hope this code will help you
<?php
/**
* FileAPI upload controller (example)
*/
include './FileAPI.class.php';
if( !empty($_SERVER['HTTP_ORIGIN']) ){
// Enable CORS
header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']);
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Range, Content-Disposition, Content-Type');
}
if( $_SERVER['REQUEST_METHOD'] == 'OPTIONS' ){
exit;
}
if( strtoupper($_SERVER['REQUEST_METHOD']) == 'POST' ){
$files = FileAPI::getFiles(); // Retrieve File List
$images = array();
// Fetch all image-info from files list
fetchImages($files, $images);
// JSONP callback name
$jsonp = isset($_REQUEST['callback']) ? trim($_REQUEST['callback']) : null;
// JSON-data for server response
$json = array(
'images' => $images
, 'data' => array('_REQUEST' => $_REQUEST, '_FILES' => $files)
);
// Server response: "HTTP/1.1 200 OK"
FileAPI::makeResponse(array(
'status' => FileAPI::OK
, 'statusText' => 'OK'
, 'body' => $json
), $jsonp);
exit;
}
function fetchImages($files, &$images, $name = 'file'){
if( isset($files['tmp_name']) ){
$filename = $files['tmp_name'];
list($mime) = explode(';', #mime_content_type($filename));
if( strpos($mime, 'image') !== false ){
$size = getimagesize($filename);
$base64 = base64_encode(file_get_contents($filename));
$images[$name] = array(
'width' => $size[0]
, 'height' => $size[1]
, 'mime' => $mime
, 'size' => filesize($filename)
, 'dataURL' => 'data:'. $mime .';base64,'. $base64
);
}
}
else {
foreach( $files as $name => $file ){
fetchImages($file, $images, $name);
}
}
}
?>
you can get the source code in here:
https://github.com/mailru/FileAPI
inside the "server" folder it is the ctrl.php and FileAPI.class.php