PHP file name and extension restoration - php

I have a bunch of files in a directory with random 16 digit file-names and no file extensions, e.g. 'RTSWZci59BqXDqaV'
I have a database that through various calls and relationships I can find the files original name, extension and content type. 'Original', as these were all once files that were uploaded to a server via a website (that I did not build).
I've written a small piece of code to loop through the files and re-write them all to their original file-name and extension. This works to a degree, in that .txt files work, but PDF / word docs are corrupted and images are scrambled / fuzzy / off-colour.
It is definitely not the case that these files are just broken.
A part of the website remains that downloads individual files:
header('Cache-Control: no-cache, must-revalidate');
header('Expires: Thu, 01 Jan 1970 00:00:00 GMT');
header('Pragma:');
header('Content-type: '.$originalContentType);
header('Content-Disposition: attachment; filename="'.$originalFileName'].'.'.$originalExt'].'"');
header('Content-Length: '.filesize(FILE_FOLDER.$fileName));
readfile(FILE_FOLDER.$fileName);
My code where I am trying to convert each file back to something use-able is some database stuff, some loops and then this:
#rename($directory.$fileName, $directory.$originalFileName.'.'.$originalExt);
I've also tried using copy(), and can't even get a single file to download by setting headers.
Is there something obvious that I should be doing differently here? Must it be the case that some other encoding happened to the image when it was uploaded? Can I do anything to these files once I've got them off of the server?

The name of a file cannot make it corrupted, other than attempting to open it with the wrong program / import filter (e.g. trying to open a JPEG as a Word Document or vice versa).
If the images look nearly right, then it sounds like some data corruption has occurred, and you have the right filenames but not the right contents.
I'm presuming you exported them from some previous location which is why they have dummy names? Perhaps during that process the files have been truncated to a particular size, or where a NULL byte was encountered. Or perhaps certain bytes have been interpreted as characters in some particular text encoding, rather than copied as-is.
If they were in a database, it's possible that you selected them using a driver or query type not designed for handling binary data.

I ended up just replicating the single header download method I described in my example, with some JS that downloaded the next file every few seconds through the browser.
By far not the most elegant solution, but it saved me some time.
<script type="text/javascript">
$(document).ready ( function(){
var delay = 1000;
<?php
$a=0;
foreach($files as $file) { ?>
delay=delay+3000;
setTimeout(function (){ window.open("<?php echo $file; ?>"); }, delay);
<?php } ?>
});
</script>

Related

using PHP to remove the extension from a file and then downloading it

I recently had a asked a question very similar to this one, however after evaluating that I did not explain it in the best way I have come back once again explaining it in a greater manner.
So, I am creating a system that will gather data from a MySQL database and use a unique id to download a file, however depending on the value of a column within that database called type, this file could be anything from a png file to an xml file. What I am currently doing is trying to download these files WITHOUT any extension.
As an example to maybe make this easier to understand, a file named image.png would be converted to just image and then downloaded.
With this you could rename the file to image.png again on the local machine and view the image.
This may seem very inefficient to most reading this but for my current situation it's all that will work.
How could I remove a files extension and then download it? (in php)
Thank you in advance.
Just use headers to specify response type.
$filepath = '/wherever/the/file/is.png';
$filename = 'new-cool-name';
header('Content-Type: whatever/content-type-is');
header("Content-disposition: attachment;filename=$filename");
readfile($filepath);
This basically sends a response with specified content-type as an attachment and the body of the attachment contains the file contents. If you never sure what's the content type is, then just use application/octet-stream
Usually when you set out to push a file for downloading from a serverside script, you do so by utilizing http headers like https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
The filename of the downloadable file is specified in that header
Okay so to remove an extention from a file you could do is
$withoutExtion = preg_replace('/\\.[^.\\s]{3,4}$/', '', $youfilename);
...followed by your file download code

Link to a PDF in html, the file has no extension, but I know it is pdf how to make it open appropriately

First post. I'm working on a project for a client where they have pdf files uploaded to a file structure (LAMP Stack) but the files have no extensions on them. Under the assumption that those files have to be PDF how would I get the browsers to understand that, and open them accordingly? Obviously with adding the file extensions this would suddenly work but I can't change the way their system works, it would result in too many changes and they are on a tight deadline. As for saving a temporary copy somewhere, I could do that, but I was hoping for a better solution. Is there a way to suggest to the browsers that they open a file a certain way?
Any thoughts guys/gals?
You just set the application type and file name in the headers, like so:
// This points to the file in question, note that it doesn't
// care whether it has an extension on the name or not.
$filePathOnDisk = '/path/to/your/pdffile';
// You can make this whatever you like, it doesn't have to
// be the same as the file name on the disk! This is the name of the file your end
// user will see when they are asked if they want to save. open, etc in the browser.
$fileName = 'file.pdf';
$data = file_get_contents($filePathOnDisk);
header("Content-type: application/pdf");
header("Content-disposition: attachment;filename=$fileName");
echo $data;
See PHP: stream remote pdf to client browser and Proper MIME media type for PDF files for reference as well.
Tested
You can use the following which will prompt the user to save the (PDF) file on their computer.
Notice the different file names.
One is the file that will be uploaded/prompted to the user download_example.pdf, while the other is the file without an extension as set in readfile('example');
<?php
header('Content-type: application/pdf');
header('Content-Disposition: attachment; filename="download_example.pdf"');
readfile('example');
?>

PHP: Unlink a little too effective

I'm in the process of developing a PHP webpage that constructs a .SVG file from a SQL database on the fly, embeds it in the page, and enables a user to interact with it. These temporary files take on the form SVG[RandomNumber].svg and the unlink function successfully deletes the files with no error messages.
Here's the problem: I assumed that if I invoked the unlink function after the SVG file had loaded for the user, the webpage would be unaffected since the user's browser would have cached the file or whatnot. Everything works perfectly when no unlink command is present in the code; however, 'unlinking' anywhere -- even at the end of the webpage, causes no object to show at all. In Firefox there's no trace of the object, and in IE I receive the error "The webpage cannot be found."
So have I deleted the file before the browser uploads it? What's the best way to deal with the general situation?
Thank you.
It might be useful to change workflow and don't create temporaries. When image is used only once or it's generation is not a big deal you can try to generate it on-the-fly in following fashion
<?php
// We'll be outputting a SVG
header('Content-type: Content-Type: image/svg+xml');
// It will be called image.svg
header('Content-Disposition: attachment; filename="image.svg"');
// Don't cache
header("Cache-Control: no-cache, must-revalidate");
header("Expires: " . date("D, j M Y H:i:s"));
// The PDF source is in original.pdf
generate_svg_from_db('image.svg');
?>

How to create xlsx file without using any excel library PHP

This is right now I am using.
$mimeType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
header('Content-Description: File Transfer');
header('Content-Type: ' . $mimeType);
header('Content-Disposition: attachment; filename='.basename($type.'.xlsx'));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
print "$header\n$data";
exit;
$header variable contains the header row of excel to be generated and looks like this
$header= "Business_Name\tBusiness_Type\tType";
separated by \t
and $data contains rows to be generated under header columns. They are also separated by \t and a row is terminated by \n.
With the current setup file is downloaded but it is not opening with ms excel and showing this message.
Excel cannot open the file "file name"
because the file format or file
extension is not valid. Verify that
the file format has not been corrupted
and that the file extension matches
the format of the file.
What header should be sent to server? or how do I generate that file?
I achieve this in a fast, sort of cheapskate way - because it's long and winded I'll just explain it in concept rather than code.
XLSX adheres to ISO 29500 which is publicly available if you want to manipulate a document thoroughly in php. Otherwise, realise that xlsx files are zipped archives of a bunch of xml files.
Make a template that you want, say it has alternating rows with styles of different types, making that in excel or an open xml editor of some description. Make sure you put some data in there, and make sure some fields are equal (just for learning purposes).
Then save your file as xlsx, rename it .zip, or open it in an archive extractor and observe the contents.
Firstly, note the [Content_Types].xml file, this describes the location of the major files in the archive and the standards to which it itself adheres and the content types of those files.
Everything outside the xl/ folder is just meta data really. But observe docProps/core.xml contains author, modification and timestamp information - which you can replace in php when you recreate this file. Also everything that is pointed to say, docProps/core.xml can be renamed to your tastes, [Content_Types].xml can't.
Okay so now you understand this, you'll begin observing ids thrown around the place. They love to use this in the file format, everything refers to everything else by its index in a particular xml property list or similar. They also usually describe the quantity of items in such lists.
In xl/ you'll see themes.xml, styles.xml, workbook.xml, sharedStrings.xml, _rels/, worksheets/.
Styles is going to be inflated with a whole lot of unnecessary styles that excel builds by default if you used it. But you should be able to see how these styles work such that you can customise your own.
Themes to me is rather pointless so I delete it and its referenced ids throughout.
Next up you'll see workbook, that's the file containing information regarding the sheets which are inside of the spreadsheet document since you can have more than 1 obviously. It also contains some sheet metadata such as its size etc.
Now comes the first big hua you'll encounter. sharedStrings.xml is a weird file which stores all the information that will be inserted into cells in a static spreadsheet. They are indexed, but the engine reading the document figures out what their indexes are. Anything which repeats can be referred back to its old index in the sheet itself (inside worksheets folder) as to save on file size in large documents with repeated values.
Not the attributes count and uniquecount in the sst element and what they obviously mean.
This is the stage in php where you populate an array of data containing what you want in your sheet, and dump it into an xml formatted list such as this file appears. Also note these files don't need to be jammed up without newlines or linefeed characters as with or without is still valid xml and they will work in readers regardless.
Check out the _rels folder, it's fairly obvious again.
Lastly is the sheet itself. The numbers in fields here refer to the indexed locations of strings in sharedStrings.xml. The attribute s is the style, t is the type of data in the field. R is the cell location though why it needs that is beyond me when it could really be figured out rather easily.
Producing this file in php shouldn't be too difficult either. Just use your indexes from your data array you used to make your sharedStrings.xml file.
Oh also sheet has column width information in it which you can figure out based on the font you used and automatically size them in php too if need be.
Lastly is the packaging of it all in php.
My code is in a class which receives data and specific saved files I created with excel to keep it simple.
$this->folder_structure_simple = Array(
"_rels/.rels" => "_rels__rels",
"docProps/app.xml" => "docProps_app_xml",
"docProps/core.xml" => "docProps_core_xml",
"xl/_rels/workbook.xml.rels",
"xl/theme/theme1.xml",
"xl/worksheets/sheet1.xml",
"xl/sharedStrings.xml",
"xl/styles.xml",
"xl/workbook.xml",
"[Content_Types].xml" => "Content_Types_xml"
);
$zip = new ZipArchive;
$res = $zip->open($this->file_name, ZipArchive::CREATE);
if($res === TRUE){
foreach($this->folder_structure_simple as $file => $function){
$zip->addFromString($file, $this->$funtion);
}
$zip->close();
echo 'ok';
}else{
return FALSE;
}
And functions produce the required data. Very fast, not very flexible.
What you have is actually a CSV file. Depending on your OS, your browser and your Excel version, then the browser will differently let you or not let your open the extensions CSV, XLS XLSX with the Excel software.
If you do want to have your data opened with Excel, then you can merge the data with an Excel template using OpenTBS. Use version 1.6.0 (or greater) which is currently in Release Candidate because it brings major facilities for Excel files.
In your title there is "no excel library PHP". I don't know why you have this specification but OpenTBS is not exactly an Excel library. It's a PHP tool for merging OpenOffice and Ms Office documents using templates.
What you have a CSV, not an XLSX file. XLSX is a ZIP-wrapped blob of XML. Change your MIME type to text/csv.

Server creates file on-the-fly for client to save

THE EXAMPLE
1) User enters in a playlist in a <textarea>
C:/music/foo.mp3
C:/music/bar.mp3
C:/music/hello.mp3
2) They click a save button. I send the user's playlist to the server with AJAX.
3) The server formats the text with PHP in this fashion:
<playlist>
<item>C:/music/foo.mp3</item>
<item>C:/music/bar.mp3</item>
<item>C:/music/hello.mp3</item>
</playlist>
4) A file save dialog pops up asking the user to save this formatted text as playlist.m3u on their own harddrive.
QUESTIONS
A) Is it possible to not write a file to the harddrive on the server when generating this m3u file? I don't want millions of files clogging up my server. I suppose PHP can echo out the formatted text and set headers to masquerade as a file.
B) How do I get the file save dialog to pop up for this on-the-fly file? If it were a real file, I would just have the PHP respond back with the location of the file. Then I would have JS insert a new iFrame with that location. But I don't want to write a file on the server, so I can't do this.
new Ajax.Request(
'generateM3u.php',
onSuccess: function(transport) {
$$('body').first().appendChild(
new Element(
'iframe', {
src: transport.responseText
}
)
);
}
);
You should take a look at http://php.net/manual/en/function.header.php from the PHP manual. There are a lot of user contributions at the bottom of the page regarding forcing the browser to show a download prompt rather than printing to screen.
Here is one from that page (By phpnet at holodyn dot com 31-Jan-2011 09:01) which I have edited slightly. I think it answers both questions A and B. Just send the textbox's contents to the PHP file through an iframe, allow it to format the text appropriately and send it back to the browser with the following headers.
$contents = '<playlist>etc....</playlist>';
header("Pragma: public"); // required
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private", false); // required for certain browsers
header("Content-Type: audio/x-mpegurl");
header("Content-Disposition: attachment; filename=\"playlist.m3u\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . strlen($contents));
ob_clean();
flush();
echo $contents;
Edit: If what you want is an all Javascript solution, then I personally don't know, and after a little google-ing, it looks like others don't either. Most seem to solve this with an invisible iframe that directs to a server-side file.
Edit 2: I've changed the content type so that it matches the m3u file type.
How about creating a form on your parent DOM, and post it to the IFRAME/pop-up that you created?
The POST action URL will be your generateMu3.php
To answer your questions,
A & B) I assume so... as long as generateM3u.php sets the correct MIMEType for the .m3u file...
I'm not familiar with syntax in PHP, but in both Java & .NET, you can set the response's MIMEType in the header to, say, a Word document, and the browser will read the header, and if it's a file that is "Save-able", it'll prompt the client to save the page as a file.
If I read this correctly there's a machine creating the .m3u files. In that case, perhaps just write the files to a temporary directory, /tmp on unix machines andC:\Windows\Temp on Windows machines. Those files are cleared on boot, which should allow you to handle B) without all the A).

Categories