I am trying to generate an Excel file with CodeIgniter and the PhpSpreadsheet library from a view. The reports that I need to make are not a list but much more complex and I can generate them more quickly with a view and sending parameters. This is my code:
$data = $this->model->bringdata();
$view = $this->load->view("data_view", $ data);
$spreadsheet = new Spreadsheet();
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet);
$writer-> save($view);
And the following error is shown
Severity: Warning
Message: fopen (): Filename can not be empty
Filename: Writer / Html.php
Line Number: 160
In the PhpSpreadsheet documentation it shows that an html page must be explicitly sent:
$writer->save("05featuredemo.htm");
Is there any way to do what I need?
Thanks for the help
It's a simple solution, you need to pass true to the third argument of load->view in order for it to return a string rather than sending it to the browser.
$view = $this->load->view("data_view", $ data, true);
Added after comment
I made the mistake of not looking into how the Html writer works.
The HTML writer writes a spreadsheet as an html file. It does not create a spreadsheet. As I understand it (and I might be mistaken) you can use \PhpOffice\PhpSpreadsheet\Reader\Html to read an html file into a spreadsheet.
In your case I think you will have to save the string returned from $this->load->view() to a temporary file. CodeIgniter's file_helper might be useful here.
You then read the temporary file to create a spreadsheet. After that you can use $writer to save the spreadsheet somewhere as any of the file types the PhpSpreadsheet supports.
$view = $this->load->view("data_view", $data, true);
$this->load->helper("file");
$fileName = "temp_file_name.html";
$path = "full/path/to/some_writable_folder/";
$path_file = $path . $fileName;
if (write_file($path_file, $view))
{
//create spreadsheet the temp html
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Html();
$spreadsheet = $reader->load($path_file);
//write out to html file
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Html($spreadsheet);
$writer->save($path."05featuredemo.htm");
//delete the temporary file
unlink($path_file);
}
else
{
//handle the failure to write the temp file
}
I haven't tested this and I'm not sure you really want to save to an html file, but I think the above is close to the answer.
Second addition
If you want to save .xlsx files you need an Xlsx writer. Use the following instead of what's shown above.
//write out to excel file
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$writer->save($ path. "doc.xlsx");
I confess that I have never used, or considered using the html reader so I'm not sure you'll get what you want. I honestly think that creating an html table so you can import it into excel is probably doing it the hard way.
Related
In Laravel I can do database transactions by passing a closure to the DB::transaction function.
Does Laravel have any support for a similar feature for the File or Storage facade? Where the file operations are run in a transaction, with rollback in case a file operation fails?
I'm imagining something like
$files = ... // Something that returns a collection
File::transaction(function () use ($files) {
$files->each(function() {
File::move(....);
});
});
There is no built in way of doing it so you'd have to make an implementation yourself.
A simple method of achieving it would be
$fileName = ""; // or $fileNames ( array ) if multiple file uploads
$files = "" // to be used if you're going to update or delete files. Again if multiple file modifications then use array
try{
/* Just a note, but your new file could overwrite any existing files,
so before uploading, check if another file exists with same filename
And if it does, load that file and keep it in the $files variable
*/
// Upload File
$fileName = // name of uploaded file
$files = // Any existing file you're going to modify. Load the entire file data, not just the name
// Modify/Delete a file
}( \Exception $e ){
// Now delete the file using fileName or $fileNames if the variable is not empty
// If you modified/deleted any file, then undo those modifications using the data in $files ( if it's not empty )
}
In this method, existing files are loaded to memory, but if there are multiple large files, it might be better to move them to a temporary location instead, and move them back if any exception is thrown. Just don't forget to delete these temporary files if the file transaction is a success
I am trying to get the image as output, but it seems like set_output() function is not working properly with content type jpeg
My Code is given below...
$image = file_get_contents('assets/images/ThinkstockPhotos-145054512_small.jpg');
$this->output->set_content_type('jpeg')->set_output($image);
When I replace the image with a plain text file, in that case, it shows me the correct output
$file = file_get_contents('assets/images/test.txt');
$this->output->set_content_type('text')->set_output($file);
I have change content type from set_content_type('jpeg') to set_content_type('jpg') and set_content_type('gif') but stil it does not work and not show me on output.
What output I am getting now is shown in screenshot given below.
I was able to replicate the issue on my localhost setup only when I provided file_get_contents either a (1) bad path or (2) a non-existent image. I think you need to provide a full path to the image as with any directory/file related operation.
Try using FCPATH that is where you index.php lies and I assume your assets/ folder as well.
public function index() {
$file = FCPATH . 'assets/images/ThinkstockPhotos-145054512_small.jpg';
if (!is_file($file)) {
exit('File not found!');
}
$image = file_get_contents($file);
$this->output->set_content_type('jpeg')->set_output($image);
}
Note: if you get File not found! then assure that the file exists in /htdocs/assets/... where /htdocs/index.php is your main CI file
I'm going to export all of my data in mySQL using PHPExcel with CSV Format. Here is my code:
function exportHartatoCSV() {
$memberid = $this->input->post('memberid');
$tahun = $this->input->post('tahunpajak');
$this->db->where('taxYear', $tahun);
$this->db->where('memberID', $memberid);
$query = $this->db->get('list_harta');
$this->load->library("excel");
$objPHPExcel = new PHPExcel();
$objPHPExcel->setActiveSheetIndex(0)
->setCellValue('A1', 'Tahun Pajak')
->setCellValue('B1', 'Pembetulan')
->setCellValue('C1', 'Kode Harta')
->setCellValue('D1', 'Jenis Harta')
->setCellValue('E1', 'Tahun Perolehan')
->setCellValue('F1', 'Harga Perolehan')
->setCellValue('G1', 'Keterangan');
$row = 2;
$workbookName = $this->MemberModel->getNPWP($memberid);
header('Content-Type: application/vnd.ms-excel');
header('Content-Disposition: attachment;filename="'.$workbookName.'.csv"');
header('Cache-Control: max-age=0');
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'CSV');
$objWriter->setDelimiter("\t");
$objWriter->setEnclosure('');
$total = 0;
foreach ($query->result() as $key) {
$objPHPExcel->getActiveSheet()->setCellValue('A'.$row, $key->taxYear);
$objPHPExcel->getActiveSheet()->setCellValue('B'.$row, $key->pembetulan);
$objPHPExcel->getActiveSheet()->setCellValue('C'.$row, $key->hartaID);
$objPHPExcel->getActiveSheet()->setCellValue('D'.$row, $key->hartaName);
$objPHPExcel->getActiveSheet()->setCellValue('E'.$row, $key->hartaYear);
$objPHPExcel->getActiveSheet()->setCellValue('F'.$row, $key->hartaPrice);
$objPHPExcel->getActiveSheet()->setCellValue('G'.$row, $key->hartaDesc);
$total += $key->hartaPrice;
$row++;
}
$objPHPExcel->getActiveSheet()->setCellValue('E'.$row, "Total Harta:");
$objPHPExcel->getActiveSheet()->setCellValue('F'.$row, $total);
$objWriter->save('php://output');
}
I want to change the delimiter from , to tab (like writing data to the next column. But it doesn't work. The strings are stick to each other. The output is like this:
Output
But I want the output is like this:
Expected Output
What I really want is a normal-looking excel table, but with .csv format.
So you need tab-delimited text (csv) file or you just want "normal"-looking excel table?
You must remember that there is a system list delimiter that is used in excel by default. In windows you can set that delimiter in your Region and Language, in Additional settings. It's called as List separator there. In some countries default value is ";", in others ",".
If you don't want to change your default delimiter you have an option to choose delimiter in Excel when you open file.
Update
You can use both options, but it looks like it's easier to set the delimiter in PHPExcel export settings to that delimiter that is set by default in your client's OS, than explain them to set custom delimiter in excel when opening file.
$objWriter->setDelimiter(";");
or
$objWriter->setDelimiter(",");
Update 2
If you have clients from different countries and/or they use different operating systems (mac/linux, windows), so they may have different system separators, then you'll need to save 2 versions of each file (with both , and ; delimiters). Or you'll need to explain them how to work with it.
I'm putting this system setting screenshots just for example:
Mac OS
Windows
As #teeyo mentioned, you might need to make sure you import it using the delimiter in Excel (or other spreadsheet editor). It might already be correct in the data generated by CI:
To verify the data is correct in the file itself, you can open it in eg. Notepad++ and use the Show All Characters feature:
Just as the title says, I want to know how to access the data from the temporary file stored by Django, when a file is uploaded, inside a view.
I want to read the data uploaded values so I can make a progress bar. My methodology is to perform a jquery getJSON request:
function update_progress_info() {
$progress.show();
$.getJSON(progress_url, function(data, status){
if (data) {
var progress = parseInt(data.uploaded) / parseInt(data.length);
var width = $progress.find('.progress-container').width()
var progress_width = width * progress;
$progress.find('.progress-bar').width(progress_width);
$progress.find('.progress-info').text('uploading ' + parseInt(progress*100) + '%');
}
window.setTimeout(update_progress_info, freq);
});
};
where progress_url is the view I have that handles the uploaded file data:
# views.py (I don't know what to do here):
def upload_progress(request):
for line in UploadedFile.temporary_file_path
response = (line)
return response
Django handles uploaded files with UploadHandler defined in settings.py with this name FILE_UPLOAD_HANDLERS that defaults to this tuple:
FILE_UPLOAD_HANDLERS =
("django.core.files.uploadhandler.MemoryFileUploadHandler",
"django.core.files.uploadhandler.TemporaryFileUploadHandler",)
The behavior with file uploads is that if the file is less than 2.5 mg then it will be kept on memory, hence, they will not be written in disk as temporary files.
If the file weights more, it will be written in chunks in the FILE_UPLOAD_TEMP_DIR in the settings.py. That's the file you'll have to query to know how many bytes have been uploaded.
You can access the uploaded/uploading files through your request variables in views like this: file = requests.FILES['file'] . There, file variable will have the type UploadedFile which contains a method temporary_file_path with the address of the file in the disk being uploaded. (Note: only files larger than 2.5 mg will have this methods) so there you may get the size of the file being uploaded.
Another way to do this is create your own UploadHandler like a ProgressBarUploadHandler and add it to your file upload handlers. This is the way the docs recommend it. Here are some snippets and tutorials for doing it.
If you need any more info the doc is really well documented.
I hope you find this helpful. Good luck.
I'm having some trouble with reading out the IPTC data of some images, the reason why I want to do this, is because my client has all the keywords already in the IPTC data and doesn't want to re-enter them on the site.
So I created this simple script to read them out:
$size = getimagesize($image, $info);
if(isset($info['APP13'])) {
$iptc = iptcparse($info['APP13']);
print '<pre>';
var_dump($iptc['2#025']);
print '</pre>';
}
This works perfectly in most cases, but it's having trouble with some images.
Notice: Undefined index: 2#025
While I can clearly see the keywords in photoshop.
Are there any decent small libraries that could read the keywords in every image? Or am I doing something wrong here?
I've seen a lot of weird IPTC problems. Could be that you have 2 APP13 segments. I noticed that, for some reasons, some JPEGs have multiple IPTC blocks. It's possibly the problem with using several photo-editing programs or some manual file manipulation.
Could be that PHP is trying to read the empty APP13 or even embedded "thumbnail metadata".
Could be also problem with segments lenght - APP13 or 8BIM have lenght marker bytes that might have wrong values.
Try HEX editor and check the file "manually".
I have found that IPTC is almost always embedded as xml using the XMP format, and is often not in the APP13 slot. You can sometimes get the IPTC info by using iptcparse($info['APP1']), but the most reliable way to get it without a third party library is to simply search through the image file from the relevant xml string (I got this from another answer, but I haven't been able to find it, otherwise I would link!):
The xml for the keywords always has the form "<dc:subject>...<rdf:Seq><rdf:li>Keyword 1</rdf:li><rdf:li>Keyword 2</rdf:li>...<rdf:li>Keyword N</rdf:li></rdf:Seq>...</dc:subject>"
So you can just get the file as a string using file_get_contents(get_attached_file($attachment_id)), use strpos() to find each opening (<rdf:li>) and closing (</rdf:li>) XML tag, and grab the keyword between them using substr().
The following snippet works for all jpegs I have tested it on. It will fill the array $keys with IPTC tags taken from an image on wordpress with id $attachment_id:
$content = file_get_contents(get_attached_file($attachment_id));
// Look for xmp data: xml tag "dc:subject" is where keywords are stored
$xmp_data_start = strpos($content, '<dc:subject>') + 12;
// Only proceed if able to find dc:subject tag
if ($xmp_data_start != FALSE) {
$xmp_data_end = strpos($content, '</dc:subject>');
$xmp_data_length = $xmp_data_end - $xmp_data_start;
$xmp_data = substr($content, $xmp_data_start, $xmp_data_length);
// Look for tag "rdf:Seq" where individual keywords are listed
$key_data_start = strpos($xmp_data, '<rdf:Seq>') + 9;
// Only proceed if able to find rdf:Seq tag
if ($key_data_start != FALSE) {
$key_data_end = strpos($xmp_data, '</rdf:Seq>');
$key_data_length = $key_data_end - $key_data_start;
$key_data = substr($xmp_data, $key_data_start, $key_data_length);
// $ctr will track position of each <rdf:li> tag, starting with first
$ctr = strpos($key_data, '<rdf:li>');
// Initialize empty array to store keywords
$keys = Array();
// While loop stores each keyword and searches for next xml keyword tag
while($ctr != FALSE && $ctr < $key_data_length) {
// Skip past the tag to get the keyword itself
$key_begin = $ctr + 8;
// Keyword ends where closing tag begins
$key_end = strpos($key_data, '</rdf:li>', $key_begin);
// Make sure keyword has a closing tag
if ($key_end == FALSE) break;
// Make sure keyword is not too long (not sure what WP can handle)
$key_length = $key_end - $key_begin;
$key_length = (100 < $key_length ? 100 : $key_length);
// Add keyword to keyword array
array_push($keys, substr($key_data, $key_begin, $key_length));
// Find next keyword open tag
$ctr = strpos($key_data, '<rdf:li>', $key_end);
}
}
}
I have this implemented in a plugin to put IPTC keywords into WP's "Description" field, which you can find here.
ExifTool is very robust if you can shell out to that (from PHP it looks like?)