Browser XML force download JavaScript [duplicate] - javascript

I have some big size PDF catalogs at my website, and I need to link these as download. When I googled, I found such a thing noted below. It should open the "Save As..." popup at link click...
<head>
<meta name="content-disposition" content="inline; filename=filename.pdf">
...
But it doesn't work :/ When I link to a file as below, it just links to file and is trying to open the file.
File name
UPDATE (according to answers below):
As I see there is no 100% reliable cross-browser solution for this. Probably the best way is using one of the web services listed below, and giving a download link...
http://box.net/
http://droplr.com/
http://getcloudapp.com/

From an answer to Force a browser to save file as after clicking link:
<a href="path/to/file" download>Click here to download</a>

Use the download attribute, but take into account that it only works for files hosted in the same origin that your code. It means that users can only download files that are from the origin site, same host.
Download with original filename:
Click here to download
Download with 'some_name' as filename:
Click here to download
Adding target="_blank" we will use a new Tab instead of the actual one, and also it will contribute to the proper behavior of the download attribute in some scenarios.
It follows the same rules as same-origin policy. You can learn more about this policy on the MDN Web Doc same-origin policy page
You can lern more about this download HTML5 attribute on the MDN Web Doc anchor's attributes page.

Meta tags are not a reliable way to achieve this result. Generally you shouldn't even do this - it should be left up to the user/user agent to decide what do to with the content you provide. The user can always force their browser to download the file if they wish to.
If you still want to force the browser to download the file, modify the HTTP headers directly. Here's a PHP code example:
$path = "path/to/file.pdf";
$filename = "file.pdf";
header('Content-Transfer-Encoding: binary'); // For Gecko browsers mainly
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($path)) . ' GMT');
header('Accept-Ranges: bytes'); // Allow support for download resume
header('Content-Length: ' . filesize($path)); // File size
header('Content-Encoding: none');
header('Content-Type: application/pdf'); // Change the mime type if the file is not PDF
header('Content-Disposition: attachment; filename=' . $filename); // Make the browser display the Save As dialog
readfile($path); // This is necessary in order to get it to actually download the file, otherwise it will be 0Kb
Note that this is just an extension to the HTTP protocol; some browsers might ignore it anyway.

I had this same issue and found a solution that has worked great so far. You put the following code in your .htaccess file:
<FilesMatch "\.(?i:pdf)$">
ForceType application/octet-stream
Header set Content-Disposition attachment
</FilesMatch>
It came from Force a File to Download Instead of Showing Up in the Browser.

I found a very simple solution for Firefox (only works with a relative rather than a direct href): add type="application/octet-stream":
Example

Generally it happens, because some browsers settings or plug-ins directly open PDF in the same window like a simple web page.
The following might help you. I have done it in PHP a few years back. But currently I'm not working on that platform.
<?php
if (isset($_GET['file'])) {
$file = $_GET['file'];
if (file_exists($file) && is_readable($file) && preg_match('/\.pdf$/',$file)) {
header('Content-type: application/pdf');
header("Content-Disposition: attachment; filename=\"$file\"");
readfile($file);
}
}
else {
header("HTTP/1.0 404 Not Found");
echo "<h1>Error 404: File Not Found: <br /><em>$file</em></h1>";
}
?>
Save the above as download.php.
Save this little snippet as a PHP file somewhere on your server and you can use it to make a file download in the browser, rather than display directly. If you want to serve files other than PDF, remove or edit line 5.
You can use it like so:
Add the following link to your HTML file.
Download the cool PDF.
Reference from: This blog

Try adding this line to your .htaccess file.
AddType application/octet-stream .pdf
I hope it'll work as it is browser independent.

I just used this, but I don't know if it works across all browsers.
It works in Firefox:
<a href="myfile.pdf" download>Click to Download</a>

A really simple way to achieve this, without using external download sites or modifying headers etc. is to simply create a ZIP file with the PDF inside and link directly to the ZIP file. This will ALWAYS trigger the Save/Open dialog, and it's still easy for people to double-click the PDF windows the program associated with .zip is launched.
BTW great question, I was looking for an answer as well, since most browser-embedded PDF plugins take sooo long to display anything (and will often hang the browser whilst the PDF is loading).

Just put the below code in your .htaccess file:
AddType application/octet-stream .csv
AddType application/octet-stream .xls
AddType application/octet-stream .doc
AddType application/octet-stream .avi
AddType application/octet-stream .mpg
AddType application/octet-stream .mov
AddType application/octet-stream .pdf
Or you can also do trick by JavaScript
element.setAttribute( 'download', whatever_string_you_want);

A very easy way to do this, if you need to force download for a single link on your page, is to use the HTML5 download-attribute in the href-link.
See: http://davidwalsh.name/download-attribute
with this you can rename the file that the user will download and at the same time it forces the download.
There has been a debate whether this is good practice or not, but in my case I have an embedded viewer for a PDF file and the viewer does not offer a download link, so i have to provide one separately. Here I want to make sure the user does not get the PDF opened in the web browser, which would be confusing.
This won't necessary open the save as-dialog, but will download the link straight to the preset download destination. And of course if you are doing a site for someone else, and need them to write in manually attributes to their links is probably a bad idea, but if there is way to get the attribute into the links, this can be a light solution.

A server-side solution is more compatible, until the "download" attribute is implemented in all the browsers.
One Python example could be a custom HTTP request handler for a filestore. The links that point to the filestore are generated like this:
http://www.myfilestore.com/filestore/13/130787e71/download_as/desiredName.pdf
Here is the code:
class HTTPFilestoreHandler(SimpleHTTPRequestHandler):
def __init__(self, fs_path, *args):
self.fs_path = fs_path # Filestore path
SimpleHTTPRequestHandler.__init__(self, *args)
def send_head(self):
# Overwrite SimpleHTTPRequestHandler.send_head to force download name
path = self.path
get_index = (path == '/')
self.log_message("path: %s" % path)
if '/download_as/' in path:
p_parts = path.split('/download_as/')
assert len(p_parts) == 2, 'Bad download link:' + path
path, download_as = p_parts
path = self.translate_path(path )
f = None
if os.path.isdir(path):
if not self.path.endswith('/'):
# Redirect browser - doing basically what Apache does
self.send_response(301)
self.send_header("Location", self.path + "/")
self.end_headers()
return None
else:
return self.list_directory(path)
ctype = self.guess_type(path)
try:
f = open(path, 'rb')
except IOError:
self.send_error(404, "File not found")
return None
self.send_response(200)
self.send_header("Content-type", ctype)
fs = os.fstat(f.fileno())
self.send_header("Expires", '0')
self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
self.send_header("Cache-Control", 'must-revalidate, post-check=0, pre-check=0')
self.send_header("Content-Transfer-Encoding", 'binary')
if download_as:
self.send_header("Content-Disposition", 'attachment; filename="%s"' % download_as)
self.send_header("Content-Length", str(fs[6]))
self.send_header("Connection", 'close')
self.end_headers()
return f
class HTTPFilestoreServer:
def __init__(self, fs_path, server_address):
def handler(*args):
newHandler = HTTPFilestoreHandler(fs_path, *args)
newHandler.protocol_version = "HTTP/1.0"
self.server = BaseHTTPServer.HTTPServer(server_address, handler)
def serve_forever(self, *args):
self.server.serve_forever(*args)
def start_server(fs_path, ip_address, port):
server_address = (ip_address, port)
httpd = HTTPFilestoreServer(fs_path, server_address)
sa = httpd.server.socket.getsockname()
print "Serving HTTP on", sa[0], "port", sa[1], "..."
httpd.serve_forever()

This is old post but here is the one my solution in JavaScript what using jQuery library.
<script>
(function($){
var download = [];
$('a.force-download, .force-download a').each(function(){
// Collect info
var $this = $(this),
$href = $this.attr('href'),
$split = $href.split('/'),
$name = document.title.replace(/[\W_]/gi, '-').replace(/-{2,}/g, '-'); // get title and clean it for the URL
// Get filename from URL
if($split[($split.length-1)])
{
$tmp = $split[($split.length-1)];
$tmp = $tmp.split('.');
$name = $tmp[0].replace(/[\W_]/gi, '-').replace(/-{2,}/g, '-');
}
// If name already exists, put timestamp there
if($.inArray($name, download) > -1)
{
$name = $name + '-' + Date.now().replace(/[\W]/gi, '-');
}
$(this).attr("download", $name);
download.push($name);
});
}(jQuery || window.jQuery))
</script>
You just need to use class force-download inside your <a> tag and will force download automaticaly. You also can add it to parent div and will pickup all links inside it.
Example:
Download PDF
This is great for WordPress and any other systems or custom websites.

Add a response header Content-Disposition:attachment; followed by the file name. Remove the Meta Content-Disposition;Inline; which will open the document in the same window
In java it is set as
response.setHeader("Content-Disposition", "attachment;filename=test.jpg");

After the file name in the HTML code I add ?forcedownload=1
This has been the simplest way for me to trigger a dialog box to save or download.

If you have a plugin within the browser which knows how to open a PDF file it will open directly. Like in case of images and HTML content.
So the alternative approach is not to send your MIME type in the response. In this way the browser will never know which plugin should open it. Hence it will give you a Save/Open dialog box.

I just had a very similar issue with the added problem that I needed to create download links to files inside a ZIP file.
I first tried to create a temporary file, then provided a link to the temporary file, but I found that some browsers would just display the contents (a CSV Excel file) rather than offering to download. Eventually I found the solution by using a servlet. It works both on Tomcat and GlassFish, and I tried it on Internet Explorer 10 and Chrome.
The servlet takes as input a full path name to the ZIP file, and the name of the file inside the zip that should be downloaded.
Inside my JSP file I have a table displaying all the files inside the zip, with links that say: onclick='download?zip=<%=zip%>&csv=<%=csv%>'
The servlet code is in download.java:
package myServlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.zip.*;
import java.util.*;
// Extend HttpServlet class
public class download extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
PrintWriter out = response.getWriter(); // now we can write to the client
String filename = request.getParameter("csv");
String zipfile = request.getParameter("zip");
String aLine = "";
response.setContentType("application/x-download");
response.setHeader( "Content-Disposition", "attachment; filename=" + filename); // Force 'save-as'
ZipFile zip = new ZipFile(zipfile);
for (Enumeration e = zip.entries(); e.hasMoreElements();) {
ZipEntry entry = (ZipEntry) e.nextElement();
if(entry.toString().equals(filename)) {
InputStream is = zip.getInputStream(entry);
BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"), 65536);
while ((aLine = br.readLine()) != null) {
out.println(aLine);
}
is.close();
break;
}
}
}
}
To compile on Tomcat you need the classpath to include tomcat\lib\servlet-api.jar or on GlassFish: glassfish\lib\j2ee.jar
But either one will work on both. You also need to set your servlet in web.xml.

Chrome 91 had a new change, it supported in chrome 86-90 and 91+.
The following syntax will make it happen.
const fileHandle = await self.showSaveFilePicker({
suggestedName: 'Untitled Text.txt',
types: [{
description: 'Text documents',
accept: {
'text/plain': ['.txt'],
},
}],
});
Read more here:
https://developer.chrome.com/blog/new-in-chrome-91/
**Another solution you can just make it as a blob and then use saveAs **
const blob = fetch("some-url-here").then(data => data.blob());
saveAs(blob, "filename.txt")

With large PDF files the browser hangs.
In Mozilla, menu Tools → Options → Applications, then next to the content type Adobe Acrobat document.
In the Action drop down, select Always ask.
This did not work for me, so what worked was:
Menu Tools* → Add-ons → Adobe Acrobat (Adobe PDF plugin for Firefox) → DISABLE.
Now I am able to download e-books!

Related

Download sharepoint list attachment in IE browser [duplicate]

I have some big size PDF catalogs at my website, and I need to link these as download. When I googled, I found such a thing noted below. It should open the "Save As..." popup at link click...
<head>
<meta name="content-disposition" content="inline; filename=filename.pdf">
...
But it doesn't work :/ When I link to a file as below, it just links to file and is trying to open the file.
File name
UPDATE (according to answers below):
As I see there is no 100% reliable cross-browser solution for this. Probably the best way is using one of the web services listed below, and giving a download link...
http://box.net/
http://droplr.com/
http://getcloudapp.com/
From an answer to Force a browser to save file as after clicking link:
<a href="path/to/file" download>Click here to download</a>
Use the download attribute, but take into account that it only works for files hosted in the same origin that your code. It means that users can only download files that are from the origin site, same host.
Download with original filename:
Click here to download
Download with 'some_name' as filename:
Click here to download
Adding target="_blank" we will use a new Tab instead of the actual one, and also it will contribute to the proper behavior of the download attribute in some scenarios.
It follows the same rules as same-origin policy. You can learn more about this policy on the MDN Web Doc same-origin policy page
You can lern more about this download HTML5 attribute on the MDN Web Doc anchor's attributes page.
Meta tags are not a reliable way to achieve this result. Generally you shouldn't even do this - it should be left up to the user/user agent to decide what do to with the content you provide. The user can always force their browser to download the file if they wish to.
If you still want to force the browser to download the file, modify the HTTP headers directly. Here's a PHP code example:
$path = "path/to/file.pdf";
$filename = "file.pdf";
header('Content-Transfer-Encoding: binary'); // For Gecko browsers mainly
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($path)) . ' GMT');
header('Accept-Ranges: bytes'); // Allow support for download resume
header('Content-Length: ' . filesize($path)); // File size
header('Content-Encoding: none');
header('Content-Type: application/pdf'); // Change the mime type if the file is not PDF
header('Content-Disposition: attachment; filename=' . $filename); // Make the browser display the Save As dialog
readfile($path); // This is necessary in order to get it to actually download the file, otherwise it will be 0Kb
Note that this is just an extension to the HTTP protocol; some browsers might ignore it anyway.
I had this same issue and found a solution that has worked great so far. You put the following code in your .htaccess file:
<FilesMatch "\.(?i:pdf)$">
ForceType application/octet-stream
Header set Content-Disposition attachment
</FilesMatch>
It came from Force a File to Download Instead of Showing Up in the Browser.
I found a very simple solution for Firefox (only works with a relative rather than a direct href): add type="application/octet-stream":
Example
Generally it happens, because some browsers settings or plug-ins directly open PDF in the same window like a simple web page.
The following might help you. I have done it in PHP a few years back. But currently I'm not working on that platform.
<?php
if (isset($_GET['file'])) {
$file = $_GET['file'];
if (file_exists($file) && is_readable($file) && preg_match('/\.pdf$/',$file)) {
header('Content-type: application/pdf');
header("Content-Disposition: attachment; filename=\"$file\"");
readfile($file);
}
}
else {
header("HTTP/1.0 404 Not Found");
echo "<h1>Error 404: File Not Found: <br /><em>$file</em></h1>";
}
?>
Save the above as download.php.
Save this little snippet as a PHP file somewhere on your server and you can use it to make a file download in the browser, rather than display directly. If you want to serve files other than PDF, remove or edit line 5.
You can use it like so:
Add the following link to your HTML file.
Download the cool PDF.
Reference from: This blog
Try adding this line to your .htaccess file.
AddType application/octet-stream .pdf
I hope it'll work as it is browser independent.
I just used this, but I don't know if it works across all browsers.
It works in Firefox:
<a href="myfile.pdf" download>Click to Download</a>
A really simple way to achieve this, without using external download sites or modifying headers etc. is to simply create a ZIP file with the PDF inside and link directly to the ZIP file. This will ALWAYS trigger the Save/Open dialog, and it's still easy for people to double-click the PDF windows the program associated with .zip is launched.
BTW great question, I was looking for an answer as well, since most browser-embedded PDF plugins take sooo long to display anything (and will often hang the browser whilst the PDF is loading).
Just put the below code in your .htaccess file:
AddType application/octet-stream .csv
AddType application/octet-stream .xls
AddType application/octet-stream .doc
AddType application/octet-stream .avi
AddType application/octet-stream .mpg
AddType application/octet-stream .mov
AddType application/octet-stream .pdf
Or you can also do trick by JavaScript
element.setAttribute( 'download', whatever_string_you_want);
A very easy way to do this, if you need to force download for a single link on your page, is to use the HTML5 download-attribute in the href-link.
See: http://davidwalsh.name/download-attribute
with this you can rename the file that the user will download and at the same time it forces the download.
There has been a debate whether this is good practice or not, but in my case I have an embedded viewer for a PDF file and the viewer does not offer a download link, so i have to provide one separately. Here I want to make sure the user does not get the PDF opened in the web browser, which would be confusing.
This won't necessary open the save as-dialog, but will download the link straight to the preset download destination. And of course if you are doing a site for someone else, and need them to write in manually attributes to their links is probably a bad idea, but if there is way to get the attribute into the links, this can be a light solution.
A server-side solution is more compatible, until the "download" attribute is implemented in all the browsers.
One Python example could be a custom HTTP request handler for a filestore. The links that point to the filestore are generated like this:
http://www.myfilestore.com/filestore/13/130787e71/download_as/desiredName.pdf
Here is the code:
class HTTPFilestoreHandler(SimpleHTTPRequestHandler):
def __init__(self, fs_path, *args):
self.fs_path = fs_path # Filestore path
SimpleHTTPRequestHandler.__init__(self, *args)
def send_head(self):
# Overwrite SimpleHTTPRequestHandler.send_head to force download name
path = self.path
get_index = (path == '/')
self.log_message("path: %s" % path)
if '/download_as/' in path:
p_parts = path.split('/download_as/')
assert len(p_parts) == 2, 'Bad download link:' + path
path, download_as = p_parts
path = self.translate_path(path )
f = None
if os.path.isdir(path):
if not self.path.endswith('/'):
# Redirect browser - doing basically what Apache does
self.send_response(301)
self.send_header("Location", self.path + "/")
self.end_headers()
return None
else:
return self.list_directory(path)
ctype = self.guess_type(path)
try:
f = open(path, 'rb')
except IOError:
self.send_error(404, "File not found")
return None
self.send_response(200)
self.send_header("Content-type", ctype)
fs = os.fstat(f.fileno())
self.send_header("Expires", '0')
self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
self.send_header("Cache-Control", 'must-revalidate, post-check=0, pre-check=0')
self.send_header("Content-Transfer-Encoding", 'binary')
if download_as:
self.send_header("Content-Disposition", 'attachment; filename="%s"' % download_as)
self.send_header("Content-Length", str(fs[6]))
self.send_header("Connection", 'close')
self.end_headers()
return f
class HTTPFilestoreServer:
def __init__(self, fs_path, server_address):
def handler(*args):
newHandler = HTTPFilestoreHandler(fs_path, *args)
newHandler.protocol_version = "HTTP/1.0"
self.server = BaseHTTPServer.HTTPServer(server_address, handler)
def serve_forever(self, *args):
self.server.serve_forever(*args)
def start_server(fs_path, ip_address, port):
server_address = (ip_address, port)
httpd = HTTPFilestoreServer(fs_path, server_address)
sa = httpd.server.socket.getsockname()
print "Serving HTTP on", sa[0], "port", sa[1], "..."
httpd.serve_forever()
This is old post but here is the one my solution in JavaScript what using jQuery library.
<script>
(function($){
var download = [];
$('a.force-download, .force-download a').each(function(){
// Collect info
var $this = $(this),
$href = $this.attr('href'),
$split = $href.split('/'),
$name = document.title.replace(/[\W_]/gi, '-').replace(/-{2,}/g, '-'); // get title and clean it for the URL
// Get filename from URL
if($split[($split.length-1)])
{
$tmp = $split[($split.length-1)];
$tmp = $tmp.split('.');
$name = $tmp[0].replace(/[\W_]/gi, '-').replace(/-{2,}/g, '-');
}
// If name already exists, put timestamp there
if($.inArray($name, download) > -1)
{
$name = $name + '-' + Date.now().replace(/[\W]/gi, '-');
}
$(this).attr("download", $name);
download.push($name);
});
}(jQuery || window.jQuery))
</script>
You just need to use class force-download inside your <a> tag and will force download automaticaly. You also can add it to parent div and will pickup all links inside it.
Example:
Download PDF
This is great for WordPress and any other systems or custom websites.
Add a response header Content-Disposition:attachment; followed by the file name. Remove the Meta Content-Disposition;Inline; which will open the document in the same window
In java it is set as
response.setHeader("Content-Disposition", "attachment;filename=test.jpg");
After the file name in the HTML code I add ?forcedownload=1
This has been the simplest way for me to trigger a dialog box to save or download.
If you have a plugin within the browser which knows how to open a PDF file it will open directly. Like in case of images and HTML content.
So the alternative approach is not to send your MIME type in the response. In this way the browser will never know which plugin should open it. Hence it will give you a Save/Open dialog box.
I just had a very similar issue with the added problem that I needed to create download links to files inside a ZIP file.
I first tried to create a temporary file, then provided a link to the temporary file, but I found that some browsers would just display the contents (a CSV Excel file) rather than offering to download. Eventually I found the solution by using a servlet. It works both on Tomcat and GlassFish, and I tried it on Internet Explorer 10 and Chrome.
The servlet takes as input a full path name to the ZIP file, and the name of the file inside the zip that should be downloaded.
Inside my JSP file I have a table displaying all the files inside the zip, with links that say: onclick='download?zip=<%=zip%>&csv=<%=csv%>'
The servlet code is in download.java:
package myServlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.zip.*;
import java.util.*;
// Extend HttpServlet class
public class download extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
PrintWriter out = response.getWriter(); // now we can write to the client
String filename = request.getParameter("csv");
String zipfile = request.getParameter("zip");
String aLine = "";
response.setContentType("application/x-download");
response.setHeader( "Content-Disposition", "attachment; filename=" + filename); // Force 'save-as'
ZipFile zip = new ZipFile(zipfile);
for (Enumeration e = zip.entries(); e.hasMoreElements();) {
ZipEntry entry = (ZipEntry) e.nextElement();
if(entry.toString().equals(filename)) {
InputStream is = zip.getInputStream(entry);
BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"), 65536);
while ((aLine = br.readLine()) != null) {
out.println(aLine);
}
is.close();
break;
}
}
}
}
To compile on Tomcat you need the classpath to include tomcat\lib\servlet-api.jar or on GlassFish: glassfish\lib\j2ee.jar
But either one will work on both. You also need to set your servlet in web.xml.
Chrome 91 had a new change, it supported in chrome 86-90 and 91+.
The following syntax will make it happen.
const fileHandle = await self.showSaveFilePicker({
suggestedName: 'Untitled Text.txt',
types: [{
description: 'Text documents',
accept: {
'text/plain': ['.txt'],
},
}],
});
Read more here:
https://developer.chrome.com/blog/new-in-chrome-91/
**Another solution you can just make it as a blob and then use saveAs **
const blob = fetch("some-url-here").then(data => data.blob());
saveAs(blob, "filename.txt")
With large PDF files the browser hangs.
In Mozilla, menu Tools → Options → Applications, then next to the content type Adobe Acrobat document.
In the Action drop down, select Always ask.
This did not work for me, so what worked was:
Menu Tools* → Add-ons → Adobe Acrobat (Adobe PDF plugin for Firefox) → DISABLE.
Now I am able to download e-books!

php://stdin reading extra characters

I am having a weird problem with a system that I am making. I am attempting to create a system to upload files to a server. I do not have direct file write privileges, and HTTP POST multipart/form-data file uploads do not work. I unfortunately do not have direct access to the server.
My solution is to use jQuery's $.ajax function to upload the file via HTTP PUT, then read it in from STDIN in PHP on the server, and upload the contents to a different location via FTP.
Everything appears to work, however somewhere along the line, extra bytes are being added. On the client, Windows claims that the file (I am testing with my website's favicon.ico file) is 7358 bytes. Once I read in the file using a standard Javascript FileReader object, it appears to be only 7311 bytes. Then, I successfully PUT it to a PHP script on the server, which claims that it read 10890 bytes, which FileZilla agrees with when I check the dump folder over FTP.
Here is my Javascript code to upload (jQuery 1.11.3 is included, and file_input is <input type="file" id="file_input"/>:
function upload(){
console.log("upload");
var f = file_input.files[0];
var r = new FileReader();
r.onload = function(event){
var d = r.result;
console.log("data loaded");
console.log(d.length);
$.ajax({
url: "upload.php?name="+encodeURIComponent(f.name)+"&mime="+encodeURIComponent(f.mime),
context: document.body,
method: "PUT",
contentType: "application/octet-stream",
data: d
}).done(function(data){
console.log("done");
console.log(data);
});
};
r.onerror = function(event){
alert("ERROR READING FILE!\nCode " + event.target.error.code);
};
r.readAsText(f);
console.log("initiated");
}
As you can see, the name and MIME type are included via the GET query string.
Here is my PHP that accepts the upload (part of upload.php):
if(strtoupper($_SERVER['REQUEST_METHOD'])=="PUT"){
echo "uploading\n";
echo "connected\n";
var_dump($_POST);
echo "x";
$file=fopen("php://input","r");
var_dump($file);
$x="";
while($byte=fgetc($file)!==false){
$x.=$byte;
}
echo strlen($x);
echo "\n";
$ftp=fopen('ftp://user:pass#example.com/admin/fs/upload/'.uniqid('',true),'w');
var_dump($ftp);
fwrite($ftp,$x);
fclose($ftp);
echo "done\n";
}
I examined the files in notepad, and the majority of the file appeared to be unchanged (there weren't any HTTP headers or anything), however the new file refused to open in any image viewer (the original would). Based on the differences in the files - only some parts are changes, and they are changed to what appear to be random non-visual characters - I would guess that it has something to do with a character encoding discrepancy. Any ideas would be appreciated.
Thanks,
MagikM18
EDIT
My server is Apache/2.4.6 and running on x86_64-redhat-linux-gnu (according to phpinfo()).
Also, I tested it with a text-based file (a PHP script) and it worked just fine - it must be something to do with the file's binary content.
For binary files (i.e. ain't text), you can try appending b to the flags of fopen:
fopen("php://input","rb");
/*
...
*/
$ftp=fopen('ftp://user:pass#example.com/admin/fs/upload/'.uniqid('',true),'wb');
Edit
You have to send files as binary too, in addition to receiving the stream. See the link in comments.

DataSnap REST JavaScript client-side – How to show pdf file data returned as TStream from a TServerMethods method

I've a DataSnap server method
function TServerMethods.GetFile(filename): TStream
returning a file.
In my test case the file is a simple .PDF.
I'm sure this function works fine, as I'm able to open files on ObjectiveC client side app's where I've used my own http call to the DataSnap method (no Delphi proxy).
The stream is read from ASIHttpRequest object and saved as local file, then loaded and regulary shown in standard pdf reader.
I do not kown how exactly ASIHttpRequest manages the returned data.
But on JavaScript client side where I use standard
stream = ServerMethods().GetFile('test.pdf')
JavaScript function, as provided from DataSnap proxy itself, I do not figure out how to show the .pdf data to the user.
Using
window.open().document.write(stream);
a new browser window opens with textual raw data ( %PDF-1.5 %âãÏÓ 1 0 obj << /Type /Catalog /Pages 2 0 R …..)
With
window.open("data:application/pdf;base64," +stream);
I get an empty new browser page.
With
window.open("data:application/pdf," +stream);
or
document.location = 'data:application/pdf,'+encodeURIComponent(serverMethods().GetFile('test'));
I get an new browser page with pdf empry reader and alert “This PDF document could not be displayed correctly”
Nothing changes adding:
GetInvocationMetadata().ResponseContentType := 'application/pdf';
into the DataSnap function.
I've no other ideas...
EDIT
The task is for a general file download, not only PDF. PDF is a test only. GetFile have to manage .pdf, .xlsx, .docx, .png, .eml, etc...
Your server side code works as expected once you set the ResponseContentType. You can test this by calling the method directly from a browser. Change the class name to match the one you're using:
http://localhost:8080/datasnap/rest/TServerMethods1/GetFile/test.pdf
I'm sure there's a way to display the stream properly on the browser side, but I'm not sure what that is. Unless you're doing something special with the stream, I'd recommend getting the document directly or using a web action and getting out of the browser's way. Basically what mjn suggested.
I can think of a couple of solutions.
1) A quick way would be to allow access to the documents directly.
In the WebFileDispatcher, add a WebFileExtension. Select .pdf and it will fill in the mime type for you. Assuming your pdf documents are in the "docs" folder, the url might look like this:
http://localhost:8080/docs/test.pdf
2) I would probably add an action on the web module. It's a little more involved, but it also gives me more control.
Using this url:
http://localhost:8080/getfile?filename=test.pdf
And code like this in the web action handler (no checking or error handling). The Content-Disposition header suggests a file name for the downloaded file:
procedure TWebModule1.WebModule1GetFileActionAction(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
lStream: TMemoryStream;
lFilename: string;
begin
lFilename := Request.QueryFields.Values['filename'];
lStream := TMemoryStream.Create;
lStream.LoadFromFile('.\Docs\' + lFilename);
lStream.Position := 0;
Response.ContentStream := lStream;
Response.ContentType := 'application/pdf';
Response.SetCustomHeader('Content-Disposition',
Format('attachment; filename="%s"', [lFilename]));
end;

Request a file with a custom header

I have an unusual requirement. Essentially I need a way so that, when the user clicks on a link or button, they will receive a PDF. The tricky part here is that the server won't process the request at all unless a custom header is sent with it (otherwise it deems the person logged out and sends them to the login screen).
At the moment the way the header works cannot be changed so please don't dwell on it; it will get changed in the future and is an internal application that I have no control over.
The options I have explored:
Using an iframe or simply opening a new window with some sort of path that will return the PDF. This can't work because I cannot specify the required header for the PDF and would be redirected before reaching the PDF itself.
Using a form and submitting the request can't work because I can't
add any custom headers to forms (only XHR and plugins can, AFAIK).
Using XHR can't work because, while it can add the header and
retrieve the file, there is no way to save it on the client side.
It would appear my only options at this point are essentially:
Use some sort of plugin such as Flash or Silverlight to request the file.
Force the change of the requirement much earlier than expected so that a header is no longer required.
Is there anything I am missing here? I'm hoping someone can either verify my findings or point me to something I missed because, as far as I can tell, there isn't really anything I can do here.
EDIT: This seems apt and confirms what I was thinking: XMLHttpRequest to open PDF in browser
Tested to work in chrome:
function toBinaryString(data) {
var ret = [];
var len = data.length;
var byte;
for (var i = 0; i < len; i++) {
byte=( data.charCodeAt(i) & 0xFF )>>> 0;
ret.push( String.fromCharCode(byte) );
}
return ret.join('');
}
var xhr = new XMLHttpRequest;
xhr.open( "GET", "/test.pdf" ); //I had test.pdf this on my local server
xhr.addEventListener( "load", function(){
var data = toBinaryString(this.responseText);
data = "data:application/pdf;base64,"+btoa(data);
document.location = data;
}, false);
xhr.setRequestHeader("magic", "header" );
xhr.overrideMimeType( "application/octet-stream; charset=x-user-defined;" );
xhr.send(null);
You can change application/pdf to application/octet-stream to have download prompt. But it's pretty easy to download from the chrome's reader as well.
In firefox nothing happens I guess it's because I don't have a plugin to deal with application/pdf installed. Changing to application/octet-stream will prompt a dl.
With IE I suppose you need some kind of VBScript/ActiveX hackery
If the file is huge, using data uri might crash the browser, in that case you can use BlobBuilder and Object URLs.
Instead of linking to the .PDF file, instead do something like
Download my eBook
which outputs a custom header, opens the PDF (binary safe) and prints the data to the user's browser, then they can choose to save the PDF despite their browser settings. The pdf_server.php should look like this:
header("Content-Type: application/octet-stream");
$file = $_GET["file"] .".pdf";
header("Content-Disposition: attachment; filename=" . urlencode($file));
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Description: File Transfer");
header("Content-Length: " . filesize($file));
flush(); // this doesn't really matter.
$fp = fopen($file, "r");
while (!feof($fp))
{
echo fread($fp, 65536);
flush(); // this is essential for large downloads
}
fclose($fp);
EDIT: The only way to add headers to a request from inside a browser (client-side) is use the XmlHttpRequest setRequestHeader method.
xhr.setRequestHeader('custom-header', 'value');

How to decode a file from base64 encoding with JavaScript

My company has a very strict intranet for work related, the net has a single doorway to allow files in and out. The doorway's security does not allow special kinds of files (*.txt, *.doc etc only), and even in those specific kinds of files, it searches for patterns that approve that the file is really that kind. (You can't simply disguise a *.zip file as a *.doc file.)
As a security project, I was told to find a way to bypass this system, and insert a single C language .exe file that says 'Hello World'.
What I thought was to change the extension to .txt, and base64 encode it so that it would be more acceptable for the system. The problem is, how to decode it once it's in. It's very easy on the outside, PHP or any other decent language can do it for me. However, in there, the only real language I have access to is JavaScript (on IE6 and maybe, MAYBE, on IE8).
So the question is as follows, can I use JavaScript to read a file from the file system, decode it, and write it back? or at least display the result for me?
Note that I don't ask for decoding/encoding a message, this one is easy, I look to decode encode a file.
JSON might be the answer you are looking for. It can actually do the trick.
Encode your txt file in JSON format. It is very likely for it to pass your company's doorway security
var myJsonData = { "text" : "SGVsbG8sIHdvcmxkIQ==" }; // <-- base64 for "Hello, world!"
Import your txt file using plain html script syntax
<script src="hello.txt" type="text/javascript"> </script>
That's it! Now you can access a JSON object using the Syntax:
alert(myJsonData.text);
To complete your job, get this simple Javascript base64 decoder.
You're done. Here's the (very simple) code I've used:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=windows-1250">
<meta name="generator" content="PSPad editor, www.pspad.com">
<title></title>
<script src="base64utils.js" type="text/javascript"> </script>
<script src="hello.txt" type="text/javascript"> </script>
<script type="text/javascript">
function helloFunction() {
document.getElementById("hello").innerHTML = decode64(myJsonData.text);
}
</script>
</head>
<body onload="helloFunction();">
<p id="hello"></p>
</body>
</html>
Using only javascript (i.e. no plugins like AIR etc), browsers don't allow access to the file system. Not only is it not possible to write a file to the disk, it's not possible to even read it - browsers are very strict on that sort of thing, thank goodness.
You cannot do this with straight JS in the browser, security context and the DOM do not allow filesystem access.
You cannot do this with current versions of flash, older versions (pre 7 IIRC) had some security flaws that allowed filesystem access.
You could do this with a custom plugin, and possibly a signed Java applet, or COM (ActiveX component, IE only).
I would suggest working with IT regarding your intranet to open up the context/permissions needed in this case as that may be the shortest path to what you are wanting here. Alternative, you could create a command-line utility to easily encrypt/decrypt given files signed by a common key.
It all depends on how you can get the file in. If you have the base-64 encoded exe as a .txt, you could easily use Flash!
I'm not quite sure how you would implement this, but you can load a file into flash and as3 using flex.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import flash.net.FileReference;
import flash.net.FileFilter;
import flash.events.IOErrorEvent;
import flash.events.Event;
import flash.utils.ByteArray;
//FileReference Class well will use to load data
private var fr:FileReference;
//File types which we want the user to open
private static const FILE_TYPES:Array = [new FileFilter("Text File", "*.txt;*.text")];
//called when the user clicks the load file button
private function onLoadFileClick():void
{
//create the FileReference instance
fr = new FileReference();
//listen for when they select a file
fr.addEventListener(Event.SELECT, onFileSelect);
//listen for when then cancel out of the browse dialog
fr.addEventListener(Event.CANCEL,onCancel);
//open a native browse dialog that filters for text files
fr.browse(FILE_TYPES);
}
/************ Browse Event Handlers **************/
//called when the user selects a file from the browse dialog
private function onFileSelect(e:Event):void
{
//listen for when the file has loaded
fr.addEventListener(Event.COMPLETE, onLoadComplete);
//listen for any errors reading the file
fr.addEventListener(IOErrorEvent.IO_ERROR, onLoadError);
//load the content of the file
fr.load();
}
//called when the user cancels out of the browser dialog
private function onCancel(e:Event):void
{
trace("File Browse Canceled");
fr = null;
}
/************ Select Event Handlers **************/
//called when the file has completed loading
private function onLoadComplete(e:Event):void
{
//get the data from the file as a ByteArray
var data:ByteArray = fr.data;
//read the bytes of the file as a string and put it in the
//textarea
outputField.text = data.readUTFBytes(data.bytesAvailable);
//clean up the FileReference instance
fr = null;
}
//called if an error occurs while loading the file contents
private function onLoadError(e:IOErrorEvent):void
{
trace("Error loading file : " + e.text);
}
]]>
</mx:Script>
<mx:Button label="Load Text File" right="10" bottom="10" click="onLoadFileClick()"/>
<mx:TextArea right="10" left="10" top="10" bottom="40" id="outputField"/>
</mx:Application>
To decode it, look into http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/utils/Base64Decoder.html
If the security system scans for patterns in files, it is very unlikely that it will overlook a base64-encoded file or base64-encoded contents in files. E-mail attachments are base64-encoded, and if the system is any good it will scan for potentially harmful e-mail attachments even if they are named .txt. The base64-encoded start of an EXE file is almost certainly recognized by it. So ISTM you are asking the wrong question.

Categories

Resources