This is kind of a follow up of this question.
This code retries a row from the database where one column is a resource and creates a file system file with it.
$query = "select top(1) DESCRIPTION, FILETYPE, DOCUMENT from dbo.Documents;";
$stmt = sqlsrv_query($this->sqlsrv_conn, $query);
if (sqlsrv_fetch($stmt)) {
$document = sqlsrv_get_field($stmt, 2, SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY));
// $fileName = sqlsrv_get_field($stmt, 0, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR));
// $ext = sqlsrv_get_field($stmt, 1, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR));
file_put_contents(
// $fileName . '.' . $ext,
'filename'.'.doc',
stream_get_contents($document),
);
}
Now I have to do this with all the records in the database, not only one. What is the best way to achieve that?
I looked at sqlsrv_fetch_array which has an argument "$fetchType" but this is to define in which format the rows are grouped together (numbered or assoc array).
How can I define the fetchType for each column under that array of rows? Like I can with sqlsrv_get_field($stmt, 2, SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY)) when only one row is fetched.
Note: I have received the feedback that my questions tend to be unclear, if you see things that can be improved to make this question better and easier to answer, please let me know.
Edit
Thank you #Zhorov the while loop does work! But I face a new silly issue.
I lied, the code I posted was not exactly the code that worked for me. Apparently I didn't test it properly with fetching the file name and extension as well. What worked was only when I fetched the binary data as a stream with nothing else.
I don't understand it at all. The following code works like expected:
$query = "select top(1) DESCRIPTION, FILETYPE, DOCUMENT from dbo.Documents;";
$stmt = sqlsrv_query($this->sqlsrv_conn, $query);
if (sqlsrv_fetch($stmt)) {
$document = sqlsrv_get_field($stmt, 2, SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY));
// $fileName = sqlsrv_get_field($stmt, 0, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR));
file_put_contents(
// $fileName . '.doc',
'filename.doc',
stream_get_contents($document),
);
}
But if I also want to fetch the file name or anything else
$query = "select top(1) DESCRIPTION, FILETYPE, DOCUMENT from dbo.Documents;";
$stmt = sqlsrv_query($this->sqlsrv_conn, $query);
if (sqlsrv_fetch($stmt)) {
$document = sqlsrv_get_field($stmt, 2, SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY));
$fileName = sqlsrv_get_field($stmt, 0);
file_put_contents(
'filename.doc',
stream_get_contents($document),
);
}
I get the following exception:
Error: stream_get_contents(): supplied resource is not a valid stream resource File
I debugged it down to this with xdebug. Here are screenshots of the two scenarios. This is the run with a valid stream and no exception (you can see the "type=stream"):
And here, the only thing I did is remove the comment on the line and a breakpoint right before the exception happens (you can see the "type=Unknown"):
Why does fetching something else break the stream? (This behaviour is the same when I using while loop or not)
Edit 2
Behaviour does change when changing the order in which sqlsrv_get_field is called. If I put the binary data after the file name, the $document variable is false. Not even an "Unknown" stream.
Edit: Reason for this is, as #Zhorov pointed out:
From documentation - sqlsrv_get_field retrieves data from the specified field of the current row. Field data must be accessed in order. For example, data from the first field cannot be accessed after data from the second field has been accessed
#Zhorov brought me on the right track for the solution.
It appears that the stream content has to be accessed last. If I get the file name before document, stream is "Unknown" and an exception is thrown. But if I get the stream at last, it works!
See for yourself. The following is the version where the stream is invalid:
And when retrieving the binary data in the column DOCUMENT last, it works:
Related
I have a database field in SQL Server 2012 database. They can contain PDF, WORD, Excel or image files. My Blob field type is varbinary(max). My code works fine using ColdFusion, however I have tried to convert it to PHP and it doesn't seem to be working. Below is the code I am using. Can someone tell me what I am doing wrong?
The example below is just for a word document and when the Word application is launched, I get a "File Conversion" dialog box asking me to select the encoding that makes the document readable none of which makes any difference. The preview window shows a bunch of garbage.
<?PHP
$aserverName = "SQ-ENT12-D01\Dev";
$aconnectionInfo = array( "Database"=>"Event_Registration", "UID"=>$uid, "PWD"=>$pwd, "CharacterSet"=>"UTF-8");
$aconn = sqlsrv_connect( $aserverName, $aconnectionInfo);
// Get the record where the binary data is
$GetBlobSQL = "select * from Congress where Congress_ID = 102";
$GetBlob = sqlsrv_query($aconn, $GetBlobSQL, array(), array('Scrollable' => 'buffered'));
$GetBlobRecordCount = sqlsrv_num_rows($GetBlob);
$brec = sqlsrv_fetch_array( $GetBlob, SQLSRV_FETCH_ASSOC, SQLSRV_SCROLL_ABSOLUTE, 0);
$WhatFile = ($brec['Document_Agenda']);
$WhatBlob = ($brec['AD_Agenda']);
$MyExt = substr($WhatFile, -4);
$WhatExt = strtolower($MyExt);
header('Content-type: application/ms-word');
header('Content-Disposition: attachment; filename="'.$WhatFile.'"');
echo $WhatBlob;
?>
I know the file or data in the database is not corrupted because my original ColdFusion code works fine to retrieve and display the data.
Try removing "CharacterSet"=>"UTF-8" from the $aconnectionInfo. You may be confusing the database and causing conversions to take place that are not needed or desired.
I am facing this problem some past days and now frustrate because I have to do it.
I need to update my CSV file columns name with database table header. My database table fields are different from CSV file. Now the problem is that first I want to update column name of CSV file with database table headers and then import its data with field mapping into database.
Please help me I don't know how I can solve this.
This is my php code:
$file = $_POST['fileName'];
$filename = "../files/" . $file;
$list = $_POST['str'];
$array_imp = implode(',', $list);
$array_exp = explode(',', $array_imp);
$fp = fopen("../files/" . $file, "w");
$num = count($fp);
for ($c = 0; $c < $num; $c++) {
if ($fp[c] !== '') {
fputcsv($fp, $array_exp);
}
}
fclose($fp);
require_once("../csv/DataSource.php");
$path = "../files/test_mysql.csv";
$dbtable = $ext[0];
$csv = new File_CSV_DataSource;
$csv->load($path);
$csvData = $csv->connect();
$res='';
foreach($csvData as $key)
{ print_r($key[1]);
$myKey ='';
$myVal='';
foreach($key as $k=>$v)
{
$myKey .=$k.',';
$myVal .="'".$v."',";
}
$myKey = substr($myKey, 0, -1);
$myVal = substr($myVal, 0, -1);
$query="insert into tablename($myKey)values($myVal)";
$res= mysql_query($query);
You have got an existing file of which the first line needs to be replaced.
This has been generally outlined here:
Overwrite Line in File with PHP
Some little explanation (and some tips that are not covered in the other question). Most often it's easier to operate with two files here:
The existing file (to be copied from)
A new file that temporarily will be used to write into.
When done, the old file will be deleted and the new file will be renamed to the name of the old file.
Your code does not work because you are already writing the new first line into the old file. That will chop-off the rest of the file when you close it.
Also you look misguided about some basic PHP features, e.g. using count on a file-handle does not help you to get the number of lines. It will just return 1.
Here is step by step what you need to do:
Open the existing file to read from. Just read the first line of it to advance the file-pointer (fgets)
Open a new file to write into. Write the new headers into it (as you already successfully do).
Copy all remaining data from the first file into the new, second file. PHP has a function for that, it is called stream_copy_to_stream.
Close both files.
Now check if the new file is what you're looking for. When this all works, you need to add some more steps:
Rename the original file to a new name. This can be done with rename.
Rename the file you've been written to to the original filename.
If you want, you then can delete the file you renamed in 5. - but only if you don't need it any longer.
And that's it. I hope this is helpful. The PHP manual contains example code for all the functions mentioned and linked. Good luck. And if you don't understand your own code, use the manual to read about it first. That reduces the places where you can introduce errors.
If you are managing to insert the table headers then you're half way there.
It sounds to me like you need to append the data after the headers something like:
$data = $headers;
if($fp[c]!=='')
{
$data .= fputcsv($fp, $array_exp);
}
Notice the dot '.' before the equals '=' in the if statement. This will add none blank $fp[c]values after the headers.
Basically what I have to do, is write a Image to the database.
As far as I understand the imagejpeg function - the output should be the plain image data, if the "string $filename" is not set or NULL.
But it does not, the only output is "1" (true)...
How do I get to the image data, without first storing the image in the filesystem and reloading it?
Here is my Code example:
// If it is the right filetype.
if ($_FILES[$dom_element]['type'] == 'image/jpeg' || $_FILES[$dom_element]['type'] == 'image/pjpeg') {
// Create the image recource.
$image_image = imagecreatefromjpeg($_FILES[$dom_element]['tmp_name']);
}
// Resize the image.
imagecopyresampled($image_image_p, $image_image, 0, 0, 0, 0, $image_new_width, $image_new_height, $image_width, $image_height);
// Write the image in the database (does not work this way -> output = "1")
mysql_select_db("$db_announcement");
$sql = "UPDATE announcement
SET announcement_guest_image_data = '" . addslashes(imagejpeg($image_image_p, NULL, $settings_image_quality)) . "',
announcement_guest_image_exists = 'yes'
WHERE announcement_timestamp = '$timestamp' AND announcement_guest_mail = '$global_mail'";
$sql = mysql_query($sql);
// Delete the image recources.
imagedestroy($image_image);
imagedestroy($image_image_p);
The documentation says about the $filename parameter:
If not set or NULL, the raw image stream will be outputted directly.
And about the return value:
Returns TRUE on success or FALSE on failure.
The function outputs the image to the standard output. It does not return it.
You can capture it from the standard output like so:
ob_start();
imagejpeg(...);
$imageData = ob_get_clean();
I would highly recommend you to avoid storing binary data, such as images in database. You can simply record everything, including the image name, date of creation, etc. on your tables, then just put that file on the file system.
I know that it is not the answer you're looking for, however I thought that it might helps.
Does anyone know how to merge (concatenate) docx documents with PHP (or Python if it's not possible in PHP)?
To clarify, my server is Linux based. I have 2 existing docx document, I need to put them in a new docx document using PHP or possibly Python.
Merging two different Docx files may be very complicated because Headers, Styles, Charts, Comments, User Modification Traces and other special contents are saved in separate inner XML sub-files in each Docx. Thus, two Docx may have different objects having the same ids. So it would be a very huge job to list all possible objects in the two documents, give them new inner ids, and re-affect them in a single one. Probably only Ms Office can do this currently.
Nevertheless, if you know that your two documents to be merged have the same styles, and if you know you have no charts, headers and other special objects, then the merging becomes something quite easy to perform.
In this case, you only have to use a Zip reader, such as TbsZip, to open the first Docx file (which is technically a zip archive containing XML sub-files) ; then read the sub-file "word/document.xml" and extract the part which is between the tags < w:body >
and < /w:body >.
In the second Docx file, open the "word/content.xml" and insert the previous content just before the tag < /w:body >. Save the result in a new Docx file.
This can be done using TbsZip, like this :
<?php
include_once('tbszip.php');
$zip = new clsTbsZip();
// Open the first document
$zip->Open('doc1.docx');
$content1 = $zip->FileRead('word/document.xml');
$zip->Close();
// Extract the content of the first document
$p = strpos($content1, '<w:body');
if ($p===false) exit("Tag <w:body> not found in document 1.");
$p = strpos($content1, '>', $p);
$content1 = substr($content1, $p+1);
$p = strpos($content1, '</w:body>');
if ($p===false) exit("Tag </w:body> not found in document 1.");
$content1 = substr($content1, 0, $p);
// Insert into the second document
$zip->Open('doc2.docx');
$content2 = $zip->FileRead('word/document.xml');
$p = strpos($content2, '</w:body>');
if ($p===false) exit("Tag </w:body> not found in document 2.");
$content2 = substr_replace($content2, $content1, $p, 0);
$zip->FileReplace('word/document.xml', $content2, TBSZIP_STRING);
// Save the merge into a third file
$zip->Flush(TBSZIP_FILE, 'merge.docx');
You may merge two Word documents with PHPDocX with a single line of code: (Source: Merging Word documents with PHPDocX)
require_once 'path /classes/DocxUtilities.inc';
$newDocx = new DocxUtilities();
$myOptions = array('mergeType' => 0);
$newDocx->mergeDocx('firstWordDoc.docx', 'secondWordDoc.docx', 'mergedWord.docx',
$myOptions);
This merging let you preserve all section structure (paper size, margins, associated footers and headers,...), includes all the required styles, manages all lists (this may seem trivial but it is not so in the OOXML standard), preserves images and charts as well as footnotes, endnotes and comments.
Moreover there is an option to preserve the original numberings (by default the page numbering continues).
One also may, via the mergeType option, to discard the section structure of the merged document and add it at the end of the first document as part of its last section. In this case, of course, the headers and footers are not imported but all other elements are still preserved.
Aspose.Words Cloud SDK for PHP can merge/join several Word Documents into a one Word document while keeping the formatting of appended or destination document depending upon the ImportFormatMode parameter value. Secondly, it is a commercial API but the free pricing plan allows 150 free monthly API Calls.
<?php
require_once('D:\xampp\htdocs\aspose-words-cloud-php-master\vendor\autoload.php');
//TODO: Get your ClientId and ClientSecret at https://dashboard.aspose.cloud (free registration is required).
$ClientSecret="xxxxxxxxxxxxxxxxxxxxxxxxxxxx";
$ClientId="xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx";
$wordsApi = new Aspose\Words\WordsApi($ClientId,$ClientSecret);
try {
$remoteDataFolder = "Temp";
$localFile = "C:/Temp/02_pages_adobe.docx";
$remoteFileName = "02_pages_adobe.docx";
$localFile1 = "C:/Temp/Sections.docx";
$remoteFileName1 = "Sections.docx";
$outputFileName = "TestAppendDocument.docx";
$uploadRequest = new Aspose\Words\Model\Requests\UploadFileRequest($localFile,$remoteDataFolder."/".$remoteFileName,null);
$wordsApi->uploadFile($uploadRequest);
$uploadRequest1 = new Aspose\Words\Model\Requests\UploadFileRequest($localFile1,$remoteDataFolder."/".$remoteFileName1,null);
$wordsApi->uploadFile($uploadRequest1);
$requestDocumentListDocumentEntries0 = new Aspose\Words\Model\DocumentEntry(array(
"href" => $remoteDataFolder . "/" . $remoteFileName1,
"import_format_mode" => "KeepSourceFormatting",
));
$requestDocumentListDocumentEntries = [
$requestDocumentListDocumentEntries0,
];
$requestDocumentList = new Aspose\Words\Model\DocumentEntryList(array(
"document_entries" => $requestDocumentListDocumentEntries,
));
$request = new Aspose\Words\Model\Requests\AppendDocumentRequest(
$remoteFileName,
$requestDocumentList,
$remoteDataFolder,
NULL,
NULL,
NULL,
$remoteDataFolder . "/" . $outputFileName,
NULL,
NULL
);
$result = $wordsApi->appendDocument($request);
##Download file
$request = new Aspose\Words\Model\Requests\DownloadFileRequest($remoteDataFolder."/".$outputFileName,NULL,NULL);
$result = $wordsApi->downloadFile($request);
copy($result->getPathName(),"AppendOutput.docx");
} catch (Exception $e) {
echo "Something went wrong: ", $e->getMessage(), "\n";
PHP_EOL;
}
?>
P.S: I'm developer evangelist at Aspose.
Is it possible to write at a particular location in a CSV file using PHP?
I don't want to append data at the end of the CSV file. But I want to add data at the end of a row already having values in the CSV.
thanks in advance
No, it s not possible to insert new data in the middle of a file, due to filesystem nature.
Only append at the end is possible.
So, the only solution is to make another file, write a beginning part of source, append a new value, and then append the rest of the source file. And finally rename a resulting file to original name.
There you go. Complete working code:
<?php
//A helping function to insert data at any position in array.
function array_insert($array, $pos, $val)
{
$array2 = array_splice($array, $pos);
$array[] = $val;
$array = array_merge($array, $array2);
return $array;
}
//What and where you want to insert
$DataToInsert = '11,Shamit,Male';
$PositionToInsert = 3;
//Full path & Name of the CSV File
$FileName = 'data.csv';
//Read the file and get is as a array of lines.
$arrLines = file($FileName);
//Insert data into this array.
$Result = array_insert($arrLines, $PositionToInsert, $DataToInsert);
//Convert result array to string.
$ResultStr = implode("\n", $Result);
//Write to the file.
file_put_contents($FileName, $ResultStr);
?>
Technically Col. Shrapnel's answer is absolutely right.
Your problem is that you don't want to deal with all these file operations just to change some data. I agree with you. But you're looking for the solution in a wrong level. Put this problem in a higher level. Create a model that represents an entity in your CSV database. Modify the model's state and call its save() method. The method should be responsible to write your model's state in CSV format.
Still, you can use a CSV library that abstracts low level operations for you. For instance, parsecsv-for-php allows you to target a specific cell:
$csv = new parseCSV();
$csv->sort_by = 'id';
$csv->parse('data.csv');
# "4" is the value of the "id" column of the CSV row
$csv->data[4]['firstname'] = 'John';
$csv->save();