I'm writing a web application that, among other things, allows users to upload files to my server. In order to prevent name clashes and to organize the files, I rename them once they are put on my server. By keeping track of the original file name I can communicate with the file's owner without them ever knowing I changed the file name on the back end. That is, until they go do download the file. In that case they're prompted to download a file with a unfamiliar name.
My question is, is there any way to specify the name of a file to be downloaded using just HTML? So a user uploads a file named 'abc.txt' and I rename it to 'xyz.txt', but when they download it I want the browser to save the file as 'abc.txt' by default. If this isn't possible with just HTML, is there any way to do it?
When they click a button to download the file, you can add the HTML5 attribute download where you can set the default filename.
That's what I did, when I created a xlsx file and the browser want to save it as zip file.
Download
Download Export
Can't find a way in HTML. I think you'll need a server-side script which will output a content-disposition header. In php this is done like this:
header('Content-Disposition: attachment; filename="downloaded.pdf"');
if you wish to provide a default filename, but not automatic download, this seems to work.
header('Content-Disposition: inline; filename="filetodownload.jpg"');
In fact, it is the server that is directly serving your files, so you have no way to interact with it from HTML, as HTML is not involved at all.
just need to use HTML5 a tag download attribute
codepen live demo
https://codepen.io/xgqfrms/full/GyEGzG/
my screen shortcut.
update answer
whether a file is downloadable depends on the server's response config, such as Content-Type, Content-Disposition;
download file's extensions are optional, depending on the server's config, too.
'Content-Type': 'application/octet-stream',
// it means unknown binary file,
// browsers usually don't execute it, or even ask if it should be executed.
'Content-Disposition': `attachment; filename=server_filename.filetype`,
// if the header specifies a filename,
// it takes priority over a filename specified in the download attribute.
download blob url file
function generatorBlobVideo(url, type, dom, link) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'arraybuffer';
xhr.onload = function(res) {
// console.log('res =', res);
var blob = new Blob(
[xhr.response],
{'type' : type},
);
// create blob url
var urlBlob = URL.createObjectURL(blob);
dom.src = urlBlob;
// download file using `a` tag
link.href = urlBlob;
};
xhr.send();
}
(function() {
var type = 'image/png';
var url = 'https://cdn.xgqfrms.xyz/logo/icon.png';
var dom = document.querySelector('#img');
var link = document.querySelector('#img-link');
generatorBlobVideo(url, type, dom, link);
})();
https://cdn.xgqfrms.xyz/HTML5/Blob/index.html
refs
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#download
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#important_mime_types_for_web_developers
Sometimes #Mephiztopheles answer won't work on blob storages and some browsers.
For this you need to use a custom function to convert the file to blob and download it
const coverntFiletoBlobAndDownload = async (file, name) => {
const blob = await fetch(file).then(r => r.blob())
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.style.display = 'none'
a.href = url
a.download = name // add custom extension here
document.body.appendChild(a)
a.click()
window.URL.revokeObjectURL(url)
}
Same code as #Hillkim Henry but with a.remove() improvement
This forces the document to remove the a tag from the body and avoid multiple elements
const coverntFiletoBlobAndDownload = async (file, name) => {
const blob = await fetch(file).then(r => r.blob())
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.style.display = 'none'
a.href = url
a.download = name // add custom extension here
document.body.appendChild(a)
a.click()
window.URL.revokeObjectURL(url)
// Remove "a" tag from the body
a.remove()
}
Well, #Palantir's answer is, for me, the most correct way!
If you plan to use that with multiple files, then i suggest you to use (or make one) PHP Download Manager.
BUT, if you want to make that to one or two files, I will suggest you the mod_rewrite option:
You have to create or edit your .htaccess file on htdocs folder and add this:
RewriteEngine on
RewriteRule ^abc\.txt$ xyz.txt
With this code, users will download xyz.txt data with the name abc.txt
NOTE: Verify if you have already the "RewriteEngine on " on your file, if yes, add only the second for each file you wish to redirect.
Good Luck ;)
(Sorry for my english)
Related
I have a static file server (made with vibe.d) serving a website that uses ES6 modules but with .mjs extension.
My browser (Chromium on Arch Linux) is throwing an error when it fetches the module files server responded with a non-JavaScript MIME type of "application/octet-stream".
It looks like I need to set the MIME type files with the .mjs from "application/octet-stream" to "application/javascript". How do I do this?
I could change all the scripts to .js but that is but I would rather figure out how to fix it right.
How would I change the MIME type for a file being fetched? Or probably better, can I change the default MIME type for all .mjs files?
Here is my d code with vibe.d:
auto router = new URLRouter;
auto fileServerSettings = new HTTPFileServerSettings;
fileServerSettings.encodingFileExtension = ["gzip" : ".gz"];
router.get("/gzip/*", serveStaticFiles("./public/", fileServerSettings));
router.get("/ws", handleWebSockets(&handleWebSocketConnection));
router.get("*", serveStaticFiles("./public/",));
listenHTTP(settings, router);
The content-type header in the response needs to be changed.
Vibe.d might have a way to configure the defaults but you can always catch it before it sends the response to edit the header of files ending in .mjs.
You can do this in vibe.d like so:
auto router = new URLRouter;
auto fileServerSettings = new HTTPFileServerSettings;
fileServerSettings.encodingFileExtension = ["gzip" : ".gz"];
fileServerSettings.preWriteCallback = &handleMIME; // Add preWriteCallback to fileServerSettings
router.get("/gzip/*", serveStaticFiles("./public/", fileServerSettings));
router.get("/ws", handleWebSockets(&handleWebSocketConnection));
router.get("*", serveStaticFiles("./public/", fileServerSettings)); // Use fileServerSettings in this get too.
// preWriteCallback, will edit the header before vibe.d sends it.
void handleMIME(scope HTTPServerRequest req, scope HTTPServerResponse res, ref string physicalPath) {
if (physicalPath.endsWith(".mjs")) {
res.contentType = "application/javascript"; // vibe.d has an easy `.contentType` attribute so you do not have to deal with the header itself.
}
}
React Native [Android]
Samsung Phone
Libraries :
react-native-document-picker [ returns our URI]
react-native-get-real-path [ converts URI to real path]
Able to :
Get a URI from local files and get real path including images
Able to get URI from Google Drive when I select a file
Unable :
Convert Google Drive URI to real path
DocumentPicker.show({filetype: [DocumentPickerUtil.allFiles()],},(error,res) => {
RNGRP.getRealPathFromURI(path).then(function(androidPath){
console.log('AndroidPath : ', androidPath);
})
}
my URI from google drive is like so :
content://com.google.android.apps.docs.storage/document/acc%3D2%3Bdoc%3D1
Fixed bug to get absolute path of Google Drive File.
So it turns out that we cannot directly get the absolute path from the URI that has been returned by selecting Google Drive File. Hence we need to apply some sort of hack to solve the problem.
What I did was, I forked the react-native-get-real-path repo into our own and then changed few things in GRP.java file.
I basically created InputStream from obtained google drive file's URI and then, using that stream, copied the file into the app's cache directory and returned the absolute path to that file and voila.
Here is the code snippet for the solution:
input = context.getContentResolver().openInputStream(uri);
/* save stream to temp file */
/* displayName is obtained from the URI */
File file = new File(context.getCacheDir(), displayName);
OutputStream output = new FileOutputStream(file);
byte[] buffer = new byte[4 * 1024]; // or other buffer size
int read;
while ((read = input.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
output.flush();
final String outputPath = file.getAbsolutePath();
return outputPath;
You can clone the git repository. Reference of https://github.com/Wraptime/react-native-get-real-path/pull/8.
It is fairly common to allow users to download a file via having some path modifier in the URL
//MVC Action to download the correct file From our Content directory
public ActionResult GetFile(string name) {
string path = this.Server.MapPath("~/Content/" + name);
byte[] file = System.IO.File.ReadAllBytes(path);
return this.File(file, "html/text");
}
quoted from http://hugoware.net/blog/dude-for-real-encrypt-your-web-config
An application I'm working with has liberal path downloads ( directory based ) sprinkled throughout the application, hence it is super vulnerable to requests like "http://localhost:1100/Home/GetFile?name=../web.config" or ( ..%2fweb.config )
Is there an easy way to restrict access to the config file - do I need to provide a custom Server.MapPath with whitelisted directories - or is there a better way.
How do you secure your file downloads - are path based downloads inherently insecure?
A simple option, assuming that all files in the ~/Content directory are safe to download would be to verify that the path is actually under (or in) the ~/Content directory and not up from it, as ~/Content/../web.config would be. I might do something like this:
// MVC Action to download the correct file From our Content directory
public ActionResult GetFile(string name) {
// Safe path
var safePath = this.Server.MapPath("~/Content");
// Requested path
string path = this.Server.MapPath("~/Content/" + name);
// Make sure requested path is safe
if (!path.StartsWith(safePath))
// NOT SAFE! Do something here, like show an error message
// Read file and return it
byte[] file = System.IO.File.ReadAllBytes(path);
return this.File(file, "html/text");
}
using (var repo = new Repository("path/to/your/repo"))
{
LibGit2Sharp.PullOptions options = new LibGit2Sharp.PullOptions();
options.FetchOptions = new FetchOptions();
options.FetchOptions.CredentialsProvider = new CredentialsHandler(
(url, usernameFromUrl, types) =>
new UsernamePasswordCredentials()
{
Username = USERNAME,
Password = PASSWORD
});
repo.Network.Pull(new LibGit2Sharp.Signature(USERNAME, EMAIL, new DateTimeOffset(DateTime.Now)), options)
}
i do not konw how to set arguments,when i use it,one error will show-----Unsupported URL protocol.could you tell me how to set arguments?
It depends on the url you are using.
For instance, issue 649 clearly states:
git.git supports relative URLs in remote configurations and resolves them relative to the working directory.
libgit2 currently fails with "Unsupported URL protocol".
It expects paths to be absolute.
So if your url is actually a local path, make sure it is an absolute path (and not a relative one).
As commented by 崔重阳, using an https instea of an sssh url is supported.
in our AIR application, I want to user to be able to download a file to a location of his choice. This can be easily done with:
var fileReference:FileReference = new FileReference();
fileReference.download( request );
The URLRequest points to a servlet http://myserver/myapp/download. If I do a navigateToUrl in our web application, the browser will properly use the filename put in the HTTP header by the server. However, in the AIR application, it will propose download as the file name for the user (because this is the last part of the URL probably).
How can I make sure the download in the AIR application will also use that name?
I am aware that the download method has an optional 2nd parameter to set the default file name, but I don't know what is in the HTTP header as file name at compile time of the client.
I managed to do it with the following code:
var stream:URLStream = new URLStream();
stream.addEventListener( HTTPStatusEvent.HTTP_RESPONSE_STATUS, function ( event:HTTPStatusEvent ):void
{
var fileName:String;
for each(var requestHeader:URLRequestHeader in event.responseHeaders)
{
if (requestHeader.name == "Content-Disposition")
{
fileName = requestHeader.value.substring( requestHeader.value.indexOf( "=" ) + 2, requestHeader.value.length - 1 );
logger.info( "Found filename in HTTP headers: {0}", [fileName] );
break;
}
}
stream.close();
logger.info("Start file download...");
var fileReference:FileReference = new FileReference();
fileReference.addEventListener(Event.COMPLETE, download_completeHandler );
fileReference.addEventListener(ProgressEvent.PROGRESS, download_progressHandler );
fileReference.download( request, fileName );
} );
stream.load( request );
I first use the URLStream class to get the HTTP headers. As soon as I have the headers, I close the stream (since it is a big file, I don't want to download the actual data just yet). From the headers, I extract the filename that is in the Content-Disposition part and use that name as the default name to pass into FileReference.download() method.