I have a Firefox addon which is able to save a piece of text to a file on the user's hard-disk.
I've been doing some research on porting this part of functionality over to Google Chrome but looks like there is a lot of challenges, examples:
How can a Chrome extension save many files to a user-specified directory?
Is there a standardized way of achieving this functionality?
I am amazed at how difficult it is to implement something so trivial.
The suggested answer above is to basically implement an extension and packaged app, but this is not an option for me as it will be available to user on internet.
And having to install 2 separate entities seem arkward.
Sample of my Firefox addon code which saves text to a file in a directory of my choice.
I'm looking for the same kind of functionality except in Google Chrome ..
var ostream;
var file = Cc["#mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
file.initWithPath('/home/rob/Desktop/');
file.append('Test.txt');
try{
if (file.exists() === false) {file.create(Ci.nsIFile.NORMAL_FILE_TYPE, 420);}
ostream = FileUtils.openFileOutputStream(file, FileUtils.MODE_WRONLY | FileUtils.MODE_APPEND);
var converter = Cc["#mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
var istream = converter.convertToInputStream('This is my string of text');
// The last argument (the callback) is optional.
NetUtil.asyncCopy(istream, ostream, function(status) {
if (!components.isSuccessCode(status)) {
alert('error');
}else{
alert('success');
}
});
} catch (e) {
return false;
}
Related
In my app I have a link to a docx file located on an server. I want to show this file in another app that can show docx files, like Google Docs or Microsoft Word.
When I display the chooser, neither Google Docs or Microsoft Word are shown (and yes they are installed), it is only the OfficeSuite app that is shown.
Anyone knows why this is?
This is my code:
var uri = android.net.Uri.parse(httpUrlString);
var extension = android.webkit.MimeTypeMap.getFileExtensionFromUrl(uri.toString());
var mimetype = android.webkit.MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
var intent = new android.content.Intent(android.content.Intent.ACTION_VIEW);
intent.setFlags(android.content.Intent.FLAG_ACTIVITY_NO_HISTORY);
if (extension === "" || mimetype == null) {
// if there is no extension or there is no definite mimetype, still try to open the file
intent.setDataAndType(uri, "text/*");
} else {
intent.setDataAndType(uri, mimetype);
}
var packageManager = application.android.context.getPackageManager();
var activities = packageManager.queryIntentActivities(intent, android.content.pm.PackageManager.MATCH_DEFAULT_ONLY);
var isIntentSafe = activities.size() > 0;
if (isIntentSafe) {
try {
application.android.startActivity.startActivity(android.content.Intent.createChooser(intent, "Ă…ben Fil med"));
} catch (e) {
console.log(e);
}
}
I have tried it on a Sony, LG and a Samsung phone, its the same.
My guess here is that you have set a default app for files with extension *.docx and this default app is OfficeSuite.
As a potential workaround you can try to change the flag from MATCH_DEFAULT_ONLY to MATCH_ALL
you can also reset your default apps on your device
how-to-set-and-clear-a-default-app-on-an-android-device
Okay, so I think I found the reason.
It looks like Google Docs app and Microsoft Word app can't handle http-uri (http://...), but if I download the file to the phone and then parse a file-uri (file:///....) it works well.
I'm developing some webGL project in Unity that has to load some external images from a directory, it runs all fine in the editor, however when I build it, it throws a Directory Not Found exception in web console. I am putting the images in Assets/StreamingAssets folder, that will become StreamingAssets folder in the built project (at root, same as index.html). Images are located there, yet browser still complains about not being able to find that directory. (I'm opening it on my own computer, no running web server)
I guess I'm missing something very obvious, but it seems like I could use some help, I've just started learning unity a week ago, and I'm not that great with C# or JavaScript (I'm trying to get better...) Is this somehow related to some javascript security issues?
Could someone please point me in the right direction, how I should be reading images(no writing need to be done) in Unity WebGL?
string appPath = Application.dataPath;
string[] filePaths = Directory.GetFiles(appPath, "*.jpg");
According to unity3d.com in webGL builds everything except threading and reflection is supported, so IO should be working - or so I thought:S
I was working around a bit and now I'm trying to load a text file containing the paths of the images (separated by ';'):
TextAsset ta = Resources.Load<TextAsset>("texManifest");
string[] lines = ta.text.Split(';');
Then I convert all lines to proper path, and add them to a list:
string temp = Application.streamingAssetsPath + "/textures/" + s;
filePaths.Add(temp);
Debug.Log tells me it looks like this:
file://////Downloads/FurnitureDresser/build/StreamingAssets/textures/79.jpg
So that seems to be allright except for all those slashes (That looks a bit odd to me)
And finally create the texture:
WWW www = new WWW("file://" + filePaths[i]);
yield return www;
Texture2D new_texture = new Texture2D(120, 80);
www.LoadImageIntoTexture(new_texture);
And around this last part (unsure: webgl projects does not seem easily debuggable) it tells me: NS_ERROR_DOM_BAD_URI: Access to restricted URI denied
Can someone please enlighten me what is happening? And most of all, what would be proper to solution to create a directory from where I can load images during runtime?
I realise this question is now a couple of years old, but, since this still appears to be commonly asked question, here is one solution (sorry, the code is C# but I am guessing the javascript implementation is similar). Basically you need to use UnityWebRequest and Coroutines to access a file from the StreamingAssets folder.
1) Create a new Loading scene (which does nothing but query the files; you could have it display some status text or a progress bar to let the user knows what is happening).
2) Add a script called Loader to the Main Camera in the Loading scene.
3) In the Loader script, add a variable to indicate whether the asset has been read successfully:
private bool isAssetRead;
4) In the Start() method of the Loading script:
void Start ()
{
// if webGL, this will be something like "http://..."
string assetPath = Application.streamingAssetsPath;
bool isWebGl = assetPath.Contains("://") ||
assetPath.Contains(":///");
try
{
if (isWebGl)
{
StartCoroutine(
SendRequest(
Path.Combine(
assetPath, "myAsset")));
}
else // desktop app
{
// do whatever you need is app is not WebGL
}
}
catch
{
// handle failure
}
}
5) In the Update() method of the Loading script:
void Update ()
{
// check to see if asset has been successfully read yet
if (isAssetRead)
{
// once asset is successfully read,
// load the next screen (e.g. main menu or gameplay)
SceneManager.LoadScene("NextScene");
}
// need to consider what happens if
// asset fails to be read for some reason
}
6) In the SendRequest() method of the Loading script:
private IEnumerator SendRequest(string url)
{
using (UnityWebRequest request = UnityWebRequest.Get(url))
{
yield return request.SendWebRequest();
if (request.isNetworkError || request.isHttpError)
{
// handle failure
}
else
{
try
{
// entire file is returned via downloadHandler
//string fileContents = request.downloadHandler.text;
// or
//byte[] fileContents = request.downloadHandler.data;
// do whatever you need to do with the file contents
if (loadAsset(fileContents))
isAssetRead = true;
}
catch (Exception x)
{
// handle failure
}
}
}
}
Put your image in the Resources folder and use Resources.Load to open the file and use it.
For example:
Texture2D texture = Resources.Load("images/Texture") as Texture2D;
if (texture != null)
{
GetComponent<Renderer>().material.mainTexture = texture;
}
The directory listing and file APIs are not available in webgl builds.
Basically no low level IO operations are supported.
We are currently using log4javascript-popUpAppender console for development and would like to store the details to local file.
Though we can use the AjaxAppender to send log messages to the server and log those messages to log4j set up with a rolling file appender, we are looking for a way to use something similar to FileAppender in Log4js.
Any idea/suggestion?
This is similar to http://www.techques.com/question/1-3626960/JavaScript-logger-into-a-rolling-file
Since we have already implemented log4javascript, we would like to stick with the same framework.
This is still not really viable in browsers, in my view. I've had another look at it; these are my observations:
In Firefox, I don't think it is currently possible to write to the local file system at all, even if the user approves. From Firefox 17 (I think), privileged code can no longer run in a web page, which rules out the old method floating around on the web (e.g. here)
IE still has its ActiveX method of doing this, but it's more locked-down than ever and requires various actions by the user to enable it.
HTML5 has a file system API which is currently only implemented by new versions of Chrome and Opera. It writes files to a carefully sandboxed location and offers no control over actual file name or path.
Safari currently has no way to do this, as far as I can tell.
In general, browsers sensibly offer little or no access to files on the local file system, so it's an unreliable way to log. However, I've written a rough BrowserFileAppender that implements the HTML5 and ActiveX methods which you're welcome to use if you find it helpful:
https://gist.github.com/timdown/6572000
Adding FileAppender solution for IE and Firefox.
function FileAppender() {}
FileAppender.prototype = new log4javascript.Appender();
FileAppender.prototype.layout = new log4javascript.SimpleLayout();
FileAppender.prototype.append = function(loggingEvent) {
var appender = this;
var getFormattedMessage = function() {
var layout = appender.getLayout();
var formattedMessage = layout.format(loggingEvent);
if (layout.ignoresThrowable()) {
formattedMessage += loggingEvent.getThrowableStrRep();
}
return formattedMessage;
};
writefile = function(destinationFile, message) {
if (isEmpty(destinationFile)) {
log.error("Source location unknown");
return;
}
if ($.browser.msie) {
try {
var fso = new ActiveXObject("Scripting.FileSystemObject");
var file = fso.OpenTextFile(destinationFile, 8, true);
file.WriteLine(message);
file.close();
} catch (e) {
log.error("Please validate if file exist");
}
} else {
netscape.security.PrivilegeManager
.enablePrivilege("UniversalXPConnect");
this.fso.initWithPath(destinationFile);
if (!this.fso.exists()) {
// create file if needed
this.fso.create(0x00, 0600);
}
var file = Components.classes["#mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
file.init(this.fso, 0x04 | 0x08 | 0x10, 064, 0);
var line = message;
file.write(line, line.length); // write data
file.close();
}
};
getFile = function() {
return "c://temp//log//Javascriptlog.log";
};
writefile(getFile(), getFormattedMessage());
};
FileAppender.prototype.toString = function() {
return "FileAppender";
};
log4javascript.FileAppender = FileAppender;
I am trying to find a way where by we can auto save a file in Firefox using JS. The way I have done till yet using FireShot on a Windows Desktop:
var element = content.document.createElement("FireShotDataElement");
element.setAttribute("Entire", EntirePage);
element.setAttribute("Action", Action);
element.setAttribute("Key", Key);
element.setAttribute("BASE64Content", "");
element.setAttribute("Data", Data);
element.setAttribute("Document", content.document);
if (typeof(CapturedFrameId) != "undefined")
element.setAttribute("CapturedFrameId", CapturedFrameId);
content.document.documentElement.appendChild(element);
var evt = content.document.createEvent("Events");
evt.initEvent("capturePageEvt", true, false);
element.dispatchEvent(evt);
But the issue is that it opens a dialog box to confirm the local drive location details. Is there a way I can hard code the local drive storage location and auto save the file?
If you are creating a Firefox add-on then FileUtils and NetUtil.asyncCopy are your friends:
Components.utils.import("resource://gre/modules/FileUtils.jsm");
Components.utils.import("resource://gre/modules/NetUtil.jsm");
var TEST_DATA = "this is a test string";
var source = Components.classes["#mozilla.org/io/string-input-stream;1"].
createInstance(Components.interfaces.nsIStringInputStream);
source.setData(TEST_DATA, TEST_DATA.length);
var file = new FileUtils.File("c:\\foo\\bar.txt");
var sink = file.openSafeFileOutputStream(file, FileUtils.MODE_WRONLY |
FileUtils.MODE_CREATE);
NetUtil.asyncCopy(source, sink);
This will asynchronously write the string this is a test string into the file c:\foo\bar.txt. Note that NetUtil.asyncCopy closes both streams automatically, you don't need to do it. However, you might want to pass a function as third parameter to this method - it will be called when the write operation is finished.
See also: Code snippets, writing to a file
Every computer has a different file structure. But still, there is a way. You can save it to cookie / session, depends on how "permanent" your data wants to be.
Do not consider writing a physical file as it requires extra permission.
If you are developing an extension for one of the mozilla applications (e.g. Firefox, Thunderbird, etc.) you define a extension id in the install.rdf.
If for some reason you need to know the extension id e.g. to retrieve the extension dir in local file system (1) or if you want to send it to a webservice (useage statistic) etc. it would be nice to get it from the install.rdf in favour to have it hardcoded in your javascript code.
But how to access the extension id from within my extension?
1) example code:
var extId = "myspecialthunderbirdextid#mydomain.com";
var filename = "install.rdf";
var file = extManager.getInstallLocation(extId).getItemFile(extId, filename);
var fullPathToFile = file.path;
I'm fairly sure the 'hard-coded ID' should never change throughout the lifetime of an extension. That's the entire purpose of the ID: it's unique to that extension, permanently. Just store it as a constant and use that constant in your libraries. There's nothing wrong with that.
What IS bad practice is using the install.rdf, which exists for the sole purpose of... well, installing. Once the extension is developed, the install.rdf file's state is irrelevant and could well be inconsistent.
"An Install Manifest is the file an Add-on Manager-enabled XUL application uses to determine information about an add-on as it is being installed" [1]
To give it an analogy, it's like accessing the memory of a deleted object from an overflow. That object still exists in memory but it's not logically longer relevant and using its data is a really, really bad idea.
[1] https://developer.mozilla.org/en/install_manifests
Like lwburk, I don't think its available through Mozilla's API's, but I have an idea which works, but it seems like a complex hack. The basic steps are:
Set up a custom resource url to point to your extension's base directory
Read the file and parse it into XML
Pull the id out using XPath
Add the following line to your chrome.manifest file
resource packagename-base-dir chrome/../
Then we can grab and parse the file with the following code:
function myId(){
var req = new XMLHttpRequest();
// synchronous request
req.open('GET', "resource://packagename-base-dir/install.rdf", false);
req.send(null);
if( req.status !== 0){
throw("file not found");
}
var data = req.responseText;
// this is so that we can query xpath with namespaces
var nsResolver = function(prefix){
var ns = {
"rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"em" : "http://www.mozilla.org/2004/em-rdf#"
};
return ns[prefix] || null;
};
var parser = CCIN("#mozilla.org/xmlextras/domparser;1", Ci.nsIDOMParser);
var doc = parser.parseFromString(data, "text/xml");
// you might have to change this xpath expression a bit to fit your setup
var myExtId = doc.evaluate("//em:targetApplication//em:id", doc, nsResolver,
Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE, null);
return myExtId.singleNodeValue.textContent;
}
I chose to use a XMLHttpRequest(as opposed to simply reading from a file) to retrieve the contents since in Firefox 4, extensions aren't necessarily unzipped. However, XMLHttpRequest will still work if the extension remains packed (haven't tested this, but have read about it).
Please note that resource URL's are shared by all installed extensions, so if packagename-base-dir isn't unique, you'll run into problems. You might be able to leverage Programmatically adding aliases to solve this problem.
This question prompted me to join StackOverflow tonight, and I'm looking forward participating more... I'll be seeing you guys around!
As Firefox now just uses Chrome's WebExtension API, you can use #serg's answer at How to get my extension's id from JavaScript?:
You can get it like this (no extra permissions required) in two
different ways:
Using runtime api: var myid = chrome.runtime.id;
Using i18n api: var myid = chrome.i18n.getMessage("##extension_id");
I can't prove a negative, but I've done some research and I don't think this is possible. Evidence:
This question, which shows that
the nsIExtensionManager interface
expects you to retrieve extension
information by ID
The full nsIExtensionManager interface
description, which shows no
method that helps
The interface does allow you to retrieve a full list of installed extensions, so it's possible to retrieve information about your extension using something other than the ID. See this code, for example:
var em = Cc['#mozilla.org/extensions/manager;1']
.getService(Ci.nsIExtensionManager);
const nsIUpdateItem = Ci.nsIUpdateItem;
var extension_type = nsIUpdateItem.TYPE_EXTENSION;
items = em.getItemList(extension_type, {});
items.forEach(function(item, index, array) {
alert(item.name + " / " + item.id + " version: " + item.version);
});
But you'd still be relying on hardcoded properties, of which the ID is the only one guaranteed to be unique.
Take a look on this add-on, maybe its author could help you, or yourself can figure out:
[Extension Manager] Extended is very
simple to use. After installing, just
open the extension manager by going to
Tools and the clicking Extensions. You
will now see next to each extension
the id of that extension.
(Not compatible yet with Firefox 4.0)
https://addons.mozilla.org/firefox/addon/2195