'初始化提交'

This commit is contained in:
2025-08-11 22:55:39 +08:00
parent 3deaf322c6
commit 9ec6daa82b
212 changed files with 45916 additions and 0 deletions

37
afterbuild.js Normal file
View File

@@ -0,0 +1,37 @@
const fs = require('fs')
// 定义要移动的平台和输出目录路径
const platform = 'android' // 平台名称如android、ios等
// 构建后的apk文件路径
const apkPath = `./platforms/${platform}/app/build/outputs/apk/debug/app-debug.apk`
// 输出目录路径
const outputPath = './build'
// 修改package.json中的version
const packageJson = require('./package.json')
;(async () => {
// 解析config.xml文件
const configXml = await fs.readFileSync('./config.xml', 'utf-8')
// 获取config.xml中widget节点的version属性
const version = configXml.match(/<widget[\s\S]*?version="([\s\S]*?)"/)[1]
// 修改package.json中的version
packageJson.version = version
await fs.writeFileSync('./package.json', JSON.stringify(packageJson, null, 2))
// 复制package.json到输出目录
await fs.copyFileSync('./package.json', `${outputPath}/package.json`)
// 删除复制后的package.json中的scripts、devDependencies、dependencies、cordova字段
const copyPackageJson = require(`${outputPath}/package.json`)
delete copyPackageJson.scripts
delete copyPackageJson.devDependencies
delete copyPackageJson.dependencies
delete copyPackageJson.cordova
await fs.writeFileSync(`${outputPath}/package.json`, JSON.stringify(copyPackageJson, null, 2))
// 移动文件并重命名
fs.rename(apkPath, `${outputPath}/${packageJson.name}.apk`, () => {})
console.info('编译成功!')
// 输出编译后的文件
console.info(`编译后的文件在${outputPath}目录下`)
})()

24
beforebuild.js Normal file
View File

@@ -0,0 +1,24 @@
// 引入babel模块
const babel = require("@babel/core");
// 引入fs模块
const fs = require("fs");
// 引入path模块
const path = require("path");
// 获取js目录下名为index.js的文件
const filePath = path.resolve(__dirname, "./www/js/index.js");
// 转译async/await
babel.transformFile(
filePath,
{
plugins: ["@babel/plugin-transform-async-to-generator"],
},
(err, result) => {
if (err) throw err;
// 把转译后的代码写入到index.build.js文件中
fs.writeFile("./www/js/index.build.js", result.code, err => {
if (err) throw err;
console.log("转译成功");
});
}
);

35
config.xml Normal file
View File

@@ -0,0 +1,35 @@
<?xml version='1.0' encoding='utf-8'?>
<widget android-packageName="cn.pandorastudio.mattlution" id="cn.pandorastudio.mattlution"
xmlns="http://www.w3.org/ns/widgets"
xmlns:cdv="http://cordova.apache.org/ns/1.0" version="2.0.3.9">
<name>问·想</name>
<description>问道,想道</description>
<author email="work@pandorastudio.cn" href="https://pandorastudio.cn">
Pandona Studio
</author>
<content src="index.html" />
<allow-intent href="https://*.pandorastudio.cn/*" />
<allow-intent href="https://*.pandorajs.com/*" />
<access origin="https://*.pandorastudio.cn" />
<access origin="https://*.pandorajs.com" />
<plugin name="cordova-plugin-vibration" />
<plugin name="cordova-plugin-file" />
<plugin name="cordova-plugin-statusbar" />
<plugin name="cordova-clipboard-plugin" />
<plugin name="cordova-plugin-file-transfer"/>
<plugin name="cordova-plugin-file-opener2" />
<plugin name="cordova-plugin-appversion" />
<icon src="res/icon/icon.png" />
<preference name="Orientation" value="portrait" />
<preference name="AndroidPersistentFileLocation" value="Compatibility" />
<preference name="AndroidExtraFilesystems" value="files,files-external,documents,sdcard,cache,cache-external,assets,root" />
<preference name="AndroidWindowSplashScreenAnimatedIcon" value="res/splash/splash.xml" />
<preference name="StatusBarBackgroundColor" value="#FFFFFF" />
<preference name="StatusBarStyle" value="default" />
<preference name="StatusBarOverlaysWebView" value="false" />
<hook type="after_build" src="afterbuild.js" />
<config-file parent="/manifest" target="AndroidManifest.xml"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
</config-file>
</widget>

5800
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

45
package.json Normal file
View File

@@ -0,0 +1,45 @@
{
"name": "cn.pandorastudio.mattlution",
"displayName": "问·想",
"version": "2.0.3.9",
"description": "问道,想道",
"scripts": {
"test": "node beforebuild.js && cordova run --emulator android",
"build": "cordova build android",
"init": "cordova platform rm android && cordova platform add android && npm run test"
},
"author": "yuantao",
"license": "Apache-2.0",
"devDependencies": {
"@babel/core": "^7.22.9",
"@babel/preset-env": "^7.22.9",
"cordova-android": "^12.0.0",
"cordova-clipboard-plugin": "^0.0.3",
"cordova-plugin-statusbar": "^3.0.0",
"cordova-plugin-vibration": "^3.1.1",
"ftp": "^0.3.10"
},
"cordova": {
"plugins": {
"cordova-plugin-vibration": {},
"cordova-plugin-file": {},
"cordova-plugin-statusbar": {},
"cordova-clipboard-plugin": {},
"com.verso.cordova.clipboard": {},
"cordova-plugin-file-transfer": {},
"cordova-plugin-file-opener2": {},
"cordova-plugin-appversion": {}
},
"platforms": [
"android"
]
},
"updateLog": {
"add": [
"版本更新日志"
],
"fix": [
"用户可以长按选择文字的问题"
]
}
}

34
plugins/android.json Normal file
View File

@@ -0,0 +1,34 @@
{
"prepare_queue": {
"installed": [],
"uninstalled": []
},
"config_munge": {
"files": {}
},
"installed_plugins": {
"com.verso.cordova.clipboard": {
"PACKAGE_NAME": "cn.pandorastudio.mattlution"
},
"cordova-plugin-appversion": {
"PACKAGE_NAME": "cn.pandorastudio.mattlution"
},
"cordova-plugin-file": {
"ANDROIDX_WEBKIT_VERSION": "1.4.0",
"PACKAGE_NAME": "cn.pandorastudio.mattlution"
},
"cordova-plugin-file-opener2": {
"PACKAGE_NAME": "cn.pandorastudio.mattlution"
},
"cordova-plugin-file-transfer": {
"PACKAGE_NAME": "cn.pandorastudio.mattlution"
},
"cordova-plugin-statusbar": {
"PACKAGE_NAME": "cn.pandorastudio.mattlution"
},
"cordova-plugin-vibration": {
"PACKAGE_NAME": "cn.pandorastudio.mattlution"
}
},
"dependent_plugins": {}
}

View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 Verso Solutions LLC
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,68 @@
Clipboard
=========
Clipboard management plugin for Cordova/PhoneGap that supports iOS, Android
Original Plugin: https://github.com/VersoSolutions/CordovaClipboard
We forked and updated this plugin to support HTML content copy & paste for both Android and iOS platform according to our needs
1. Support added to copy html content
2. Support added to copy/paste plain text from copied html content.
## Usage
Install the plugin using the CLI, for instance with PhoneGap:
cordova plugin add https://github.com/Anuj-Raghuvanshi/cordova-clipboard-plugin
The plugin creates the object `cordova.plugins.clipboard` with the methods `copy(text, onSuccess, onError)` and `paste(onSuccess, onError)`.
Example:
var text = "Hello World!";
cordova.plugins.clipboard.copy(text);
cordova.plugins.clipboard.paste(function (text) { alert(text); });
## Notes
### All platforms
- The plugin only works with text content.
### Windows Phone
- The Windows Phone platform doesn't allow applications to read the content of the clipboard. Using the `paste` method will return an error.
### Android
- The minimum supported API Level is 11. Make sure that `minSdkVersion` is larger or equal to 11 in `AndroidManifest.xml`.
## Acknowledgements
This plugin was inspired by [ClipboardManagerPlugin](https://github.com/jacob/ClipboardManagerPlugin) (Android) and [ClipboardPlugin](https://github.com/phonegap/phonegap-plugins/tree/master/iPhone/ClipboardPlugin) (iOS).
## License
The MIT License (MIT)
Copyright (c) 2013 Verso Solutions LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,34 @@
{
"name": "cordova-clipboard-plugin",
"version": "0.0.3",
"description": "Clipboard management plugin for Cordova/PhoneGap",
"cordova": {
"id": "com.verso.cordova.clipboard",
"platforms": [
"ios",
"android"
]
},
"repository": {
"type": "git",
"url": "git+https://github.com/Anuj-Raghuvanshi/cordova-clipboard-plugin"
},
"keywords": [
"clipboard",
"copy",
"paste",
"ecosystem:cordova",
"cordova-ios",
"cordova-android"
],
"engines": [{
"name": "cordova",
"version": ">=3.0.0"
}],
"author": "",
"license": "MIT",
"bugs": {
"url": "https://github.com/VersoSolutions/CordovaClipboard/issues"
},
"homepage": "https://github.com/VersoSolutions/CordovaClipboard#readme"
}

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="com.verso.cordova.clipboard"
version="0.1.0">
<engines>
<engine name="cordova" version=">=3.0.0" />
</engines>
<name>Clipboard</name>
<description>Clipboard management plugin for Cordova/PhoneGap</description>
<author>Verso Solutions LLC</author>
<keywords>clipboard,copy,paste</keywords>
<license>MIT</license>
<js-module src="www/clipboard.js" name="Clipboard">
<clobbers target="cordova.plugins.clipboard" />
</js-module>
<!-- iOS -->
<platform name="ios">
<config-file target="config.xml" parent="/*">
<feature name="Clipboard">
<param name="ios-package" value="CDVClipboard" />
</feature>
</config-file>
<header-file src="src/ios/CDVClipboard.h" />
<source-file src="src/ios/CDVClipboard.m" />
</platform>
<!-- Android -->
<platform name="android">
<source-file src="src/android/Clipboard.java" target-dir="src/com/verso/cordova/clipboard" />
<config-file target="res/xml/config.xml" parent="/*">
<feature name="Clipboard">
<param name="android-package" value="com.verso.cordova.clipboard.Clipboard" />
</feature>
</config-file>
</platform>
<!-- Windows Phone 8 -->
<platform name="wp8">
<config-file target="config.xml" parent="/*">
<feature name="Clipboard">
<param name="wp-package" value="Clipboard"/>
</feature>
</config-file>
<source-file src="src/wp8/Clipboard.cs" />
</platform>
</plugin>

View File

@@ -0,0 +1,65 @@
package com.verso.cordova.clipboard;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.PluginResult;
import org.apache.cordova.CallbackContext;
import org.json.JSONArray;
import org.json.JSONException;
import android.content.Context;
import android.content.ClipboardManager;
import android.content.ClipData;
import android.content.ClipDescription;
import android.text.Html;
public class Clipboard extends CordovaPlugin {
private static final String actionCopy = "copy";
private static final String actionPaste = "paste";
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
ClipboardManager clipboard = (ClipboardManager) cordova.getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
if (action.equals(actionCopy)) {
try {
String text = args.getString(0);
//ClipData clip = ClipData.newPlainText("Text", text);
String plainText = Html.fromHtml(text).toString();
ClipData clip = ClipData.newHtmlText("html", plainText, text);
clipboard.setPrimaryClip(clip);
callbackContext.success(text);
return true;
} catch (JSONException e) {
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
} catch (Exception e) {
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, e.toString()));
}
} else if (action.equals(actionPaste)) {
//if (!clipboard.getPrimaryClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {
if (!clipboard.getPrimaryClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_HTML)) {
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.NO_RESULT));
}
try {
ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0);
String text = item.getText().toString();
if (text == null) text = "";
callbackContext.success(text);
return true;
} catch (Exception e) {
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, e.toString()));
}
}
return false;
}
}

View File

@@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>
#import <Cordova/CDVPlugin.h>
@interface CDVClipboard : CDVPlugin {}
- (void)copy:(CDVInvokedUrlCommand*)command;
- (void)paste:(CDVInvokedUrlCommand*)command;
@end

View File

@@ -0,0 +1,36 @@
#import <Foundation/Foundation.h>
#import <Cordova/CDVPlugin.h>
#import <Cordova/CDVPluginResult.h>
#import <MobileCoreServices/MobileCoreServices.h>
#import "CDVClipboard.h"
@implementation CDVClipboard
- (void)copy:(CDVInvokedUrlCommand*)command {
[self.commandDelegate runInBackground:^{
NSString *html = [command.arguments objectAtIndex:0];
NSData *data = [html dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *dict = @{@"WebMainResource": @{@"WebResourceData": data, @"WebResourceFrameName": @"", @"WebResourceMIMEType": @"text/html", @"WebResourceTextEncodingName": @"UTF-8", @"WebResourceURL": @"about:blank"}};
data = [NSPropertyListSerialization dataWithPropertyList:dict format:NSPropertyListXMLFormat_v1_0 options:0 error:nil];
NSString *archive = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSAttributedString *decodedString = [[NSAttributedString alloc] initWithData:data
options:@{NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType}
documentAttributes:NULL
error:NULL];
[UIPasteboard generalPasteboard].items = @[@{@"Apple Web Archive pasteboard type": archive, (id)kUTTypeUTF8PlainText: [decodedString string]}];
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:archive];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
}
- (void)paste:(CDVInvokedUrlCommand*)command {
[self.commandDelegate runInBackground:^{
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
NSString *text = [pasteboard valueForPasteboardType:@"public.text"];
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:text];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
}
@end

View File

@@ -0,0 +1,58 @@
using WPCordovaClassLib.Cordova;
using WPCordovaClassLib.Cordova.Commands;
using WPCordovaClassLib.Cordova.JSON;
namespace Cordova.Extension.Commands
{
public class Clipboard : BaseCommand
{
public void copy(string options)
{
string text = "";
try
{
text = JsonHelper.Deserialize<string[]>(options)[0];
}
catch
{
DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
return;
}
try
{
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() =>
{
System.Windows.Clipboard.SetText(text);
});
DispatchCommandResult(new PluginResult(PluginResult.Status.OK, text));
}
catch
{
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
}
}
public void paste(string options)
{
string text = "";
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() =>
{
try
{
text = System.Windows.Clipboard.GetText();
DispatchCommandResult(new PluginResult(PluginResult.Status.OK, text));
}
catch
{
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
}
});
}
}
}

View File

@@ -0,0 +1,34 @@
var cordova = require('cordova');
/**
* Clipboard plugin for Cordova
*
* @constructor
*/
function Clipboard () {}
/**
* Sets the clipboard content
*
* @param {String} text The content to copy to the clipboard
* @param {Function} onSuccess The function to call in case of success (takes the copied text as argument)
* @param {Function} onFail The function to call in case of error
*/
Clipboard.prototype.copy = function (text, onSuccess, onFail) {
if (typeof text === "undefined" || text === null) text = "";
cordova.exec(onSuccess, onFail, "Clipboard", "copy", [text]);
};
/**
* Gets the clipboard content
*
* @param {Function} onSuccess The function to call in case of success
* @param {Function} onFail The function to call in case of error
*/
Clipboard.prototype.paste = function (onSuccess, onFail) {
cordova.exec(onSuccess, onFail, "Clipboard", "paste", []);
};
// Register the plugin
var clipboard = new Clipboard();
module.exports = clipboard;

View File

@@ -0,0 +1,19 @@
Copyright (c) 2015 Rareloop Ltd
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,31 @@
# Cordova App Version Plugin
Cordova/PhoneGap plugin for accessing the native app's version and build number within JavaScript. The values are bootstrapped at app load and can be used synchronously.
## Supported Platforms
- iOS
- Android
## Installation
The plugin can be installed with the Cordova CLI:
```shell
cordova plugin add cordova-plugin-appversion
```
The plugin is hosted on NPM so requires Cordova CLI `5.0.0` as a minimum. If you have any issues installing, make sure you have the most upto date version of Cordova from NPM:
```shell
npm install -g cordova
```
## Usage
After `deviceReady` has fired you'll be able to access a new object in the global scope that contains both the app version and build number.
```javascript
console.log(AppVersion.version); // e.g. "1.2.3"
console.log(AppVersion.build); // e.g. 1234
```

View File

@@ -0,0 +1,29 @@
{
"name": "cordova-plugin-appversion",
"version": "1.0.0",
"description": "Access the native app version & build number in JavaScript",
"cordova": {
"id": "cordova-plugin-appversion",
"platforms": [
"android",
"ios"
]
},
"repository": {
"type": "git",
"url": "https://github.com/Rareloop/cordova-plugin-app-version.git"
},
"keywords": [
"cordova",
"appversion",
"ecosystem:cordova",
"cordova-ios",
"cordova-android"
],
"author": "Rareloop (http://rareloop.com)",
"license": "MIT",
"bugs": {
"url": "https://github.com/Rareloop/cordova-plugin-app-version/issues"
},
"homepage": "https://github.com/Rareloop/cordova-plugin-app-version"
}

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
id="cordova-plugin-appversion" version="1.0.0">
<name>App Version</name>
<description>Expose the native app version to JavaScript</description>
<license>MIT</license>
<js-module src="www/app-version.js" name="RareloopAppVersion">
<clobbers target="AppVersion" />
</js-module>
<platform name="ios">
<config-file target="config.xml" parent="/*">
<feature name="RareloopAppVersion">
<param name="ios-package" value="RareloopAppVersion"/>
</feature>
</config-file>
<header-file src="src/ios/RareloopAppVersion.h" />
<source-file src="src/ios/RareloopAppVersion.m" />
</platform>
<platform name="android">
<config-file target="config.xml" parent="/*">
<feature name="RareloopAppVersion">
<param name="android-package" value="com.rareloop.cordova.appversion.RareloopAppVersion"/>
</feature>
</config-file>
<source-file src="src/android/RareloopAppVersion.java" target-dir="src/com/rareloop/cordova/appversion" />
</platform>
</plugin>

View File

@@ -0,0 +1,81 @@
/**
* Copyright (c) 2015 Rareloop Ltd
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.rareloop.cordova.appversion;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.PluginResult;
import android.util.TypedValue;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManager;
import android.util.Log;
/**
* Cordova plugin that allows for an arbitrarly sized and positioned WebView to be shown ontop of the canvas
*/
public class RareloopAppVersion extends CordovaPlugin {
private static final String TAG = "RareloopAppVersion";
/**
* Executes the request and returns PluginResult
*
* @param action
* @param args
* @param callbackContext
* @return boolean
* @throws JSONException
*/
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
/**
* appVersion
*/
if (action.equals("getAppVersion")) {
try {
PackageManager packageManager = this.cordova.getActivity().getPackageManager();
JSONObject r = new JSONObject();
r.put("version", packageManager.getPackageInfo(this.cordova.getActivity().getPackageName(), 0).versionName);
r.put("build", packageManager.getPackageInfo(this.cordova.getActivity().getPackageName(), 0).versionCode);
callbackContext.success(r);
} catch (NameNotFoundException e) {
callbackContext.error("Exception thrown");
}
return true;
}
// Default response to say the action hasn't been handled
return false;
}
}

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) 2015 Rareloop Ltd
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import <Cordova/CDV.h>
@interface RareloopAppVersion : CDVPlugin <UIWebViewDelegate>
{
}
- (void)getAppVersion:(CDVInvokedUrlCommand*)command;
@end

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2015 Rareloop Ltd
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#import "RareloopAppVersion.h"
#import <Cordova/CDV.h>
@implementation RareloopAppVersion
/**
* Get the app version and build number
*
* @param {CDVInvokedUrlCommand*} command [description]
*/
- (void)getAppVersion:(CDVInvokedUrlCommand*)command
{
NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
NSString *appVersion = [infoDict objectForKey:@"CFBundleShortVersionString"];
NSNumber *buildNumber = [infoDict objectForKey:@"CFBundleVersion"];
// Build a plugin response
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary: @{@"version": appVersion, @"build": buildNumber}];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
@end

View File

@@ -0,0 +1,68 @@
/**
* Copyright (c) 2015 Rareloop Ltd
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
var exec = require('cordova/exec');
var channel = require('cordova/channel');
var utils = require('cordova/utils');
channel.createSticky('onCordovaAppVersionReady');
// Wait on the onCordovaAppVersionReady event
channel.waitForInitialization('onCordovaAppVersionReady');
/**
* Object representing the app's native version and build number
* @constructor
*/
var RareloopAppVersion = function () {
this.version = null;
this.build = null;
this.available = false;
var _this = this;
channel.onCordovaReady.subscribe(function() {
_this.getInfo(function(info) {
_this.available = true;
_this.version = info.version;
_this.build = parseInt(info.build, 10);
channel.onCordovaAppVersionReady.fire();
},function(e) {
_this.available = false;
utils.alert("[ERROR] Error initializing Version Plugin: " + e);
});
});
};
/**
* Get the app version
*
* @param {Function} successCallback The function to call when the heading data is available
* @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL)
*/
RareloopAppVersion.prototype.getInfo = function(successCallback, errorCallback) {
exec(successCallback, errorCallback, "RareloopAppVersion", "getAppVersion", []);
};
// Export the module
module.exports = new RareloopAppVersion();

View File

@@ -0,0 +1,44 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
### Expected Behaviour
### Actual Behaviour
### Reproduce Scenario (including but not limited to)
#### Steps to Reproduce
#### Platform and Version (eg. Android 5.0 or iOS 9.2.1)
#### (Android) What device vendor (e.g. Samsung, HTC, Sony...)
#### Cordova CLI info
cordova info
Here is the output:
#### Plugin version
cordova plugin version | grep cordova-plugin-file-opener2
Here is the output:
#### Sample Code that illustrates the problem
#### Logs taken while reproducing problem
Run
`adb logcat PluginManager:V CordovaPlugin:V CordovaLog:V chromium:V *:S`
while testing the bug in your app to get some output from your error. This will help you to understand more about the nature of your problem.

View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 pwlin - pwlin05@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,191 @@
# A File Opener Plugin for Cordova
[![Latest Stable Version](https://img.shields.io/npm/v/cordova-plugin-file-opener2.svg)](https://www.npmjs.com/package/cordova-plugin-file-opener2) [![Total Downloads](https://img.shields.io/npm/dt/cordova-plugin-file-opener2.svg)](https://npm-stat.com/charts.html?package=cordova-plugin-file-opener2)
This plugin will open a file on your device file system with its default application.
```js
cordova.plugins.fileOpener2.open(
filePath,
fileMIMEType,
{
error : function(){ },
success : function(){ }
}
);
```
## Installation
```shell
$ cordova plugin add cordova-plugin-file-opener2
```
## Requirements
The following platforms and versions are supported by the latest release:
- Android 5.1+ / iOS 9+ / Windows / Electron
- Cordova CLI 7.0 or higher
Cordova CLI 6.0 is supported by 2.0.19, but there are a number of issues, particularly with Android builds (see [232](https://github.com/pwlin/cordova-plugin-file-opener2/issues/232) [203](https://github.com/pwlin/cordova-plugin-file-opener2/issues/203) [207](https://github.com/pwlin/cordova-plugin-file-opener2/issues/207)). Using the [cordova-android-support-gradle-release](https://github.com/dpa99c/cordova-android-support-gradle-release) plugin may help.
## fileOpener2.open(filePath, mimeType, options)
Opens a file
### Supported Platforms
- Android 5.1+
- iOS 9+
- Windows
- Electron
### Quick Examples
Open an APK install dialog:
```javascript
cordova.plugins.fileOpener2.open(
'/Downloads/gmail.apk',
'application/vnd.android.package-archive'
);
```
Open a PDF document with the default PDF reader and optional callback object:
```js
cordova.plugins.fileOpener2.open(
'/Download/starwars.pdf', // You can also use a Cordova-style file uri: cdvfile://localhost/persistent/Downloads/starwars.pdf
'application/pdf',
{
error : function(e) {
console.log('Error status: ' + e.status + ' - Error message: ' + e.message);
},
success : function () {
console.log('file opened successfully');
}
}
);
```
__Note on Electron:__ Do not forget to enable Node.js in your app by adding `"nodeIntegration": true` to `platforms/electron/platform_www/cdv-electron-settings.json` file, See [Cordova-Electron documentation](https://cordova.apache.org/docs/en/latest/guide/platforms/electron/index.html#customizing-the-application's-window-options).
### Market place installation
Install From Market: to install an APK from a market place, such as Google Play or the App Store, you can use an `<a>` tag in combination with the `market://` protocol:
```html
<a href="market://details?id=xxxx" target="_system">Install from Google Play</a>
<a href="itms-apps://itunes.apple.com/app/my-app/idxxxxxxxx?mt=8" target="_system">Install from App Store</a>
```
or in code:
```js
window.open("[market:// or itms-apps:// link]","_system");
```
## fileOpener2.showOpenWithDialog(filePath, mimeType, options)
Opens with system modal to open file with an already installed app.
### Supported Platforms
- Android 5.1+
- iOS 9+
### Quick Example
```js
cordova.plugins.fileOpener2.showOpenWithDialog(
'/Downloads/starwars.pdf', // You can also use a Cordova-style file uri: cdvfile://localhost/persistent/Downloads/starwars.pdf
'application/pdf',
{
error : function(e) {
console.log('Error status: ' + e.status + ' - Error message: ' + e.message);
},
success : function () {
console.log('file opened successfully');
},
position : [0, 0]
}
);
```
`position` array of coordinates from top-left device screen, use for iOS dialog positioning.
## fileOpener2.uninstall(packageId, callbackContext)
Uninstall a package with its ID.
__Note__: You need to add `<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />` to your `AndroidManifest.xml`
### Supported Platforms
- Android 5.1+
### Quick Example
```js
cordova.plugins.fileOpener2.uninstall('com.zynga.FarmVille2CountryEscape', {
error : function(e) {
console.log('Error status: ' + e.status + ' - Error message: ' + e.message);
},
success : function() {
console.log('Uninstall intent activity started.');
}
});
```
## fileOpener2.appIsInstalled(packageId, callbackContext)
Check if an app is already installed.
### Supported Platforms
- Android 5.1+
### Quick Example
```javascript
cordova.plugins.fileOpener2.appIsInstalled('com.adobe.reader', {
success : function(res) {
if (res.status === 0) {
console.log('Adobe Reader is not installed.');
} else {
console.log('Adobe Reader is installed.')
}
}
});
```
---
## Android APK installation limitation
The following limitations apply when opening an APK file for installation:
- On Android 8+, your application must have the `ACTION_INSTALL_PACKAGE` permission. You can add it by adding this to your app's `config.xml` file:
```xml
<platform name="android">
<config-file parent="/manifest" target="AndroidManifest.xml" xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
</config-file>
</platform>
```
- Before Android 7, you can only install APKs from the "external" partition. For example, you can install from `cordova.file.externalDataDirectory`, but **not** from `cordova.file.dataDirectory`. Android 7+ does not have this limitation.
---
## SD card limitation on Android
It is not always possible to open a file from the SD Card using this plugin on Android. This is because the underlying Android library used [does not support serving files from secondary external storage devices](https://stackoverflow.com/questions/40318116/fileprovider-and-secondary-external-storage). Whether or not your the SD card is treated as a secondary external device depends on your particular phone's set up.
---
## Notes
- For properly opening _any_ file, you must already have a suitable reader for that particular file type installed on your device. Otherwise this will not work.
- [It is reported](https://github.com/pwlin/cordova-plugin-file-opener2/issues/2#issuecomment-41295793) that in iOS, you might need to remove `<preference name="iosPersistentFileLocation" value="Library" />` from your `config.xml`
- If you are wondering what MIME-type should you pass as the second argument to `open` function, [here is a list of all known MIME-types](http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?view=co)
---

View File

@@ -0,0 +1,46 @@
{
"name": "cordova-plugin-file-opener2",
"version": "4.0.0",
"description": "A File Opener Plugin for Cordova. (The Original Version)",
"cordova": {
"id": "cordova-plugin-file-opener2",
"platforms": [
"android",
"ios",
"windows",
"electron"
]
},
"repository": {
"type": "git",
"url": "https://github.com/pwlin/cordova-plugin-file-opener2.git"
},
"keywords": [
"ecosystem:cordova",
"cordova-android",
"cordova-ios",
"cordova-windows",
"cordova-electron"
],
"engines": {
"cordovaDependencies": {
"2.0.0": {
"cordova": ">=6.0.0"
},
"3.0.0": {
"cordova": ">=7.0.0"
},
"4.0.0": {
"cordova-android": ">=10.0.0"
}
}
},
"author": {
"name": "pwlin05@gmail.com"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/pwlin/cordova-plugin-file-opener2/issues"
},
"homepage": "https://github.com/pwlin/cordova-plugin-file-opener2#readme"
}

View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8" ?>
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-file-opener2" version="4.0.0">
<name>File Opener2</name>
<description>A File Opener Plugin for Cordova. (The Original Version)</description>
<license>MIT</license>
<js-module src="www/plugins.FileOpener2.js" name="FileOpener2">
<clobbers target="cordova.plugins.fileOpener2" />
</js-module>
<!-- Android -->
<platform name="android">
<source-file src="src/android/io/github/pwlin/cordova/plugins/fileopener2/FileOpener2.java" target-dir="src/io/github/pwlin/cordova/plugins/fileopener2" />
<source-file src="src/android/io/github/pwlin/cordova/plugins/fileopener2/FileProvider.java" target-dir="src/io/github/pwlin/cordova/plugins/fileopener2" />
<config-file target="res/xml/config.xml" parent="/*">
<feature name="FileOpener2">
<param name="android-package" value="io.github.pwlin.cordova.plugins.fileopener2.FileOpener2" />
</feature>
</config-file>
<config-file target="AndroidManifest.xml" parent="/*">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
</config-file>
<config-file target="AndroidManifest.xml" parent="application">
<provider android:name="io.github.pwlin.cordova.plugins.fileopener2.FileProvider" android:authorities="${applicationId}.fileOpener2.provider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/opener_paths" />
</provider>
</config-file>
<source-file src="src/android/res/xml/opener_paths.xml" target-dir="res/xml" />
</platform>
<!-- iOS -->
<platform name="ios">
<config-file target="config.xml" parent="/*">
<feature name="FileOpener2">
<param name="ios-package" value="FileOpener2" />
</feature>
</config-file>
<source-file src="src/ios/FileOpener2.m" />
<header-file src="src/ios/FileOpener2.h" />
</platform>
<!-- WP8 -->
<platform name="wp8">
<config-file target="config.xml" parent="/*">
<feature name="FileOpener2">
<param name="wp-package" value="FileOpener2" />
</feature>
</config-file>
<source-file src="src/wp8/FileOpener2.cs" />
</platform>
<!-- windows -->
<platform name="windows">
<js-module src="src/windows/fileOpener2Proxy.js" name="fileOpener2Proxy">
<merges target="" />
</js-module>
</platform>
<!-- browser -->
<platform name="browser">
<config-file parent="/*" target="config.xml">
<feature name="FileOpener2">
<param name="browser-package" value="FileOpener2"/>
</feature>
</config-file>
<!-- Required for browserify: we always link module below as there is conditional reference
to this module from requestFileSystem and resolveLocalFileSystemURI modules. -->
<js-module src="www/browser/isChrome.js" name="isChrome">
<runs />
</js-module>
<js-module src="src/browser/FileSaver.min.js" name="FileSaver">
<clobbers target="FileSaver"/>
</js-module>
<js-module src="src/browser/FileOpener2.js" name="FileOpener2Proxy">
<runs/>
</js-module>
</platform>
<!-- electron -->
<platform name="electron">
<js-module src="src/electron/FileOpener2.js" name="fileOpener2">
<merges target="" />
</js-module>
</platform>
</plugin>

View File

@@ -0,0 +1,199 @@
/*
The MIT License (MIT)
Copyright (c) 2013 pwlin - pwlin05@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.github.pwlin.cordova.plugins.fileopener2;
import java.io.File;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Build;
import android.webkit.MimeTypeMap;
import io.github.pwlin.cordova.plugins.fileopener2.FileProvider;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.PluginResult;
import org.apache.cordova.CordovaResourceApi;
public class FileOpener2 extends CordovaPlugin {
/**
* Executes the request and returns a boolean.
*
* @param action
* The action to execute.
* @param args
* JSONArry of arguments for the plugin.
* @param callbackContext
* The callback context used when calling back into JavaScript.
* @return boolean.
*/
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
if (action.equals("open")) {
String fileUrl = args.getString(0);
String contentType = args.getString(1);
Boolean openWithDefault = true;
if(args.length() > 2){
openWithDefault = args.getBoolean(2);
}
this._open(fileUrl, contentType, openWithDefault, callbackContext);
}
else if (action.equals("uninstall")) {
this._uninstall(args.getString(0), callbackContext);
}
else if (action.equals("appIsInstalled")) {
JSONObject successObj = new JSONObject();
if (this._appIsInstalled(args.getString(0))) {
successObj.put("status", PluginResult.Status.OK.ordinal());
successObj.put("message", "Installed");
}
else {
successObj.put("status", PluginResult.Status.NO_RESULT.ordinal());
successObj.put("message", "Not installed");
}
callbackContext.success(successObj);
}
else {
JSONObject errorObj = new JSONObject();
errorObj.put("status", PluginResult.Status.INVALID_ACTION.ordinal());
errorObj.put("message", "Invalid action");
callbackContext.error(errorObj);
}
return true;
}
private void _open(String fileArg, String contentType, Boolean openWithDefault, CallbackContext callbackContext) throws JSONException {
String fileName = "";
try {
CordovaResourceApi resourceApi = webView.getResourceApi();
Uri fileUri = resourceApi.remapUri(Uri.parse(fileArg));
fileName = fileUri.getPath();
} catch (Exception e) {
fileName = fileArg;
}
File file = new File(fileName);
if (file.exists()) {
try {
if (contentType == null || contentType.trim().equals("")) {
contentType = _getMimeType(fileName);
}
Intent intent;
if (contentType.equals("application/vnd.android.package-archive")) {
// https://stackoverflow.com/questions/9637629/can-we-install-an-apk-from-a-contentprovider/9672282#9672282
intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
Uri path;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
path = Uri.fromFile(file);
} else {
Context context = cordova.getActivity().getApplicationContext();
path = FileProvider.getUriForFile(context, cordova.getActivity().getPackageName() + ".fileOpener2.provider", file);
}
intent.setDataAndType(path, contentType);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_NEW_TASK);
} else {
intent = new Intent(Intent.ACTION_VIEW);
Context context = cordova.getActivity().getApplicationContext();
Uri path = FileProvider.getUriForFile(context, cordova.getActivity().getPackageName() + ".fileOpener2.provider", file);
intent.setDataAndType(path, contentType);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
/*
* @see
* http://stackoverflow.com/questions/14321376/open-an-activity-from-a-cordovaplugin
*/
if(openWithDefault){
cordova.getActivity().startActivity(intent);
}
else{
cordova.getActivity().startActivity(Intent.createChooser(intent, "Open File in..."));
}
callbackContext.success();
} catch (android.content.ActivityNotFoundException e) {
JSONObject errorObj = new JSONObject();
errorObj.put("status", PluginResult.Status.ERROR.ordinal());
errorObj.put("message", "Activity not found: " + e.getMessage());
callbackContext.error(errorObj);
}
} else {
JSONObject errorObj = new JSONObject();
errorObj.put("status", PluginResult.Status.ERROR.ordinal());
errorObj.put("message", "File not found");
callbackContext.error(errorObj);
}
}
private String _getMimeType(String url) {
String mimeType = "*/*";
int extensionIndex = url.lastIndexOf('.');
if (extensionIndex > 0) {
String extMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(url.substring(extensionIndex+1));
if (extMimeType != null) {
mimeType = extMimeType;
}
}
return mimeType;
}
private void _uninstall(String packageId, CallbackContext callbackContext) throws JSONException {
if (this._appIsInstalled(packageId)) {
Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
intent.setData(Uri.parse("package:" + packageId));
cordova.getActivity().startActivity(intent);
callbackContext.success();
}
else {
JSONObject errorObj = new JSONObject();
errorObj.put("status", PluginResult.Status.ERROR.ordinal());
errorObj.put("message", "This package is not installed");
callbackContext.error(errorObj);
}
}
private boolean _appIsInstalled(String packageId) {
PackageManager pm = cordova.getActivity().getPackageManager();
boolean appInstalled = false;
try {
pm.getPackageInfo(packageId, PackageManager.GET_ACTIVITIES);
appInstalled = true;
} catch (PackageManager.NameNotFoundException e) {
appInstalled = false;
}
return appInstalled;
}
}

View File

@@ -0,0 +1,29 @@
/*
The MIT License (MIT)
Copyright (c) 2013 pwlin - pwlin05@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.github.pwlin.cordova.plugins.fileopener2;
/*
* http://stackoverflow.com/questions/40746144/error-with-duplicated-fileprovider-in-manifest-xml-with-cordova/41550634#41550634
*/
public class FileProvider extends androidx.core.content.FileProvider {
}

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://developer.android.com/reference/android/support/v4/content/FileProvider.html#SpecifyFiles -->
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!-- cordova.file.dataDirectory -->
<files-path name="files" path="." />
<!-- cordova.file.cacheDirectory -->
<cache-path name="cache" path="." />
<!-- cordova.file.externalDataDirectory -->
<external-files-path name="external-files" path="." />
<!-- cordova.file.externalCacheDirectory -->
<external-cache-path name="external-cache" path="." />
<!-- cordova.file.externalRootDirectory -->
<external-path name="external" path="." />
</paths>

View File

@@ -0,0 +1,125 @@
/*
The MIT License (MIT)
Copyright (c) 2019 fefc - fefc.dev@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
const cacheDirectory = (require('./isChrome')()) ? 'filesystem:' + window.location.origin + '/temporary/' : 'file:///temporary/';
const dataDirectory = (require('./isChrome')()) ? 'filesystem:' + window.location.origin + '/persistent/' : 'file:///persistent/';
function open(successCallback, errorCallback, data) {
var fullFilePath = data[0];
//var contentType = data[1]; //Not needed in browser
//var openDialog = data[2]; //Not needed in browser
var dirPath = fullFilePath.substring(0, fullFilePath.lastIndexOf('/') + 1);
var fileName = fullFilePath.substring(fullFilePath.lastIndexOf('/') + 1, fullFilePath.length);
var fileSystemLocalPath = getLocalPathAndFileSystem(dirPath);
if (!fileSystemLocalPath.error) {
window.requestFileSystem(fileSystemLocalPath.fileSystem, 0, (fs) => {
readFile(fs.root, fileSystemLocalPath.localPath + fileName).then((blob) => {
FileSaver.saveAs(blob, fileName);
successCallback();
}).catch((error) => {
errorCallback(error);
});
}, (error) => {
errorCallback(error);
});
} else {
errorCallback('INVALID_PATH');
}
}
/**
*
* Gets the localPath according to the fileSystem (TEMPORARY or PERSISTENT).
*
* @param {String} Path to the file or directory to check
* @returns {Object} value with informations to requestFileSystem later
* @returns {string} value.localPath The localPath in relation with fileSystem.
* @returns {number} value.fileSystem the fileSystem (TEMPORARY or PERSISTENT).
* @returns {error} value.error if the path is not valid.
* @returns {message} value.message error message.
*/
function getLocalPathAndFileSystem(pathToCheck) {
let ret = {
localPath: '',
fileSystem: window.TEMPORARY
};
if (pathToCheck.startsWith(cacheDirectory)) {
ret.localPath = pathToCheck.replace(cacheDirectory, '');
ret.fileSystem = window.TEMPORARY;
} else if (pathToCheck.startsWith(dataDirectory)) {
ret.localPath = pathToCheck.replace(dataDirectory, '');
ret.fileSystem = window.PERSISTENT;
} else {
return {error: true, message: 'INVALID_PATH'};
}
if (!ret.localPath.endsWith('/')) ret.localPath += '/';
return ret;
}
/**
*
* Reads a file in the fileSystem as an DataURL.
*
* @param {String} Root is the root folder of the fileSystem.
* @param {String} Path is the file to be red.
* @returns {Promise} which resolves with an Object containing DataURL, rejects if something went wrong.
*/
function readFile(root, filePath) {
return new Promise((resolve, reject) => {
if (filePath.startsWith('/')) filePath = filePath.substring(1);
root.getFile(filePath, {}, (fileEntry) => {
fileEntry.file((file) => {
let reader = new FileReader();
reader.onload = function() {
resolve(reader.result);
};
reader.onerror = function() {
reject(reader.error);
}
reader.readAsDataURL(file);
}, (error) => {
reject(error);
});
}, (error) => {
reject(error);
});
});
}
module.exports = {
open: open
};
require( "cordova/exec/proxy" ).add( "FileOpener2", module.exports );

View File

@@ -0,0 +1,3 @@
(function(a,b){if("function"==typeof define&&define.amd)define([],b);else if("undefined"!=typeof exports)b();else{b(),a.FileSaver={exports:{}}.exports}})(this,function(){"use strict";function b(a,b){return"undefined"==typeof b?b={autoBom:!1}:"object"!=typeof b&&(console.warn("Deprecated: Expected third argument to be a object"),b={autoBom:!b}),b.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(a.type)?new Blob(["\uFEFF",a],{type:a.type}):a}function c(b,c,d){var e=new XMLHttpRequest;e.open("GET",b),e.responseType="blob",e.onload=function(){a(e.response,c,d)},e.onerror=function(){console.error("could not download file")},e.send()}function d(a){var b=new XMLHttpRequest;b.open("HEAD",a,!1);try{b.send()}catch(a){}return 200<=b.status&&299>=b.status}function e(a){try{a.dispatchEvent(new MouseEvent("click"))}catch(c){var b=document.createEvent("MouseEvents");b.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),a.dispatchEvent(b)}}var f="object"==typeof window&&window.window===window?window:"object"==typeof self&&self.self===self?self:"object"==typeof global&&global.global===global?global:void 0,a=f.saveAs||("object"!=typeof window||window!==f?function(){}:"download"in HTMLAnchorElement.prototype?function(b,g,h){var i=f.URL||f.webkitURL,j=document.createElement("a");g=g||b.name||"download",j.download=g,j.rel="noopener","string"==typeof b?(j.href=b,j.origin===location.origin?e(j):d(j.href)?c(b,g,h):e(j,j.target="_blank")):(j.href=i.createObjectURL(b),setTimeout(function(){i.revokeObjectURL(j.href)},4E4),setTimeout(function(){e(j)},0))}:"msSaveOrOpenBlob"in navigator?function(f,g,h){if(g=g||f.name||"download","string"!=typeof f)navigator.msSaveOrOpenBlob(b(f,h),g);else if(d(f))c(f,g,h);else{var i=document.createElement("a");i.href=f,i.target="_blank",setTimeout(function(){e(i)})}}:function(a,b,d,e){if(e=e||open("","_blank"),e&&(e.document.title=e.document.body.innerText="downloading..."),"string"==typeof a)return c(a,b,d);var g="application/octet-stream"===a.type,h=/constructor/i.test(f.HTMLElement)||f.safari,i=/CriOS\/[\d]+/.test(navigator.userAgent);if((i||g&&h)&&"undefined"!=typeof FileReader){var j=new FileReader;j.onloadend=function(){var a=j.result;a=i?a:a.replace(/^data:[^;]*;/,"data:attachment/file;"),e?e.location.href=a:location=a,e=null},j.readAsDataURL(a)}else{var k=f.URL||f.webkitURL,l=k.createObjectURL(a);e?e.location=l:location.href=l,e=null,setTimeout(function(){k.revokeObjectURL(l)},4E4)}});f.saveAs=a.saveAs=a,"undefined"!=typeof module&&(module.exports=a)});
//# sourceMappingURL=FileSaver.min.js.map

View File

@@ -0,0 +1,35 @@
/*
The MIT License (MIT)
Copyright (c) 2020 pwlin - pwlin05@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// https://www.electronjs.org/docs/api/shell
const { shell } = global.require('electron');
module.exports = {
open: function (onSuccess, onError, fileName) {
var opn = shell.openItem(fileName[0]);
if (opn === true) {
onSuccess(true);
} else {
onError({'status': 0, 'message': 'Failed opening file.'});
}
}
};
require('cordova/exec/proxy').add('FileOpener2', module.exports);

View File

@@ -0,0 +1,35 @@
/*
The MIT License (MIT)
Copyright (c) 2013 pwlin - pwlin05@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import <Cordova/CDV.h>
@interface FileOpener2 : CDVPlugin <UIDocumentInteractionControllerDelegate> {
NSString *localFile;
}
@property(nonatomic, strong) UIDocumentInteractionController *controller;
@property(nonatomic, strong) CDVViewController *cdvViewController;
- (void) open: (CDVInvokedUrlCommand*)command;
@end

View File

@@ -0,0 +1,151 @@
/*
The MIT License (MIT)
Copyright (c) 2013 pwlin - pwlin05@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import "FileOpener2.h"
#import <Cordova/CDV.h>
#import <QuartzCore/QuartzCore.h>
#import <MobileCoreServices/MobileCoreServices.h>
@implementation FileOpener2
@synthesize controller = docController;
CDVPluginResult* pluginResult = nil;
NSString* callbackId = nil;
- (void) open: (CDVInvokedUrlCommand*)command {
callbackId = command.callbackId;
NSString *path = [command.arguments objectAtIndex:0];
NSString *contentType = [command.arguments objectAtIndex:1];
BOOL showPreview = YES;
if ([command.arguments count] >= 3) {
showPreview = [[command.arguments objectAtIndex:2] boolValue];
}
CDVViewController* cont = (CDVViewController*)[super viewController];
self.cdvViewController = cont;
NSString *uti = nil;
if ([contentType length] == 0) {
NSArray *dotParts = [path componentsSeparatedByString:@"."];
NSString *fileExt = [dotParts lastObject];
uti = (__bridge NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)fileExt, NULL);
} else {
uti = (__bridge NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef)contentType, NULL);
}
dispatch_async(dispatch_get_main_queue(), ^{
NSURL *fileURL = NULL;
NSString *decodedPath = [path stringByRemovingPercentEncoding];
if ([path isEqualToString:decodedPath]) {
NSLog(@"Path parameter not encoded. Building file URL encoding it...");
fileURL = [NSURL fileURLWithPath:[path stringByReplacingOccurrencesOfString:@"file://" withString:@""]];;
} else {
NSLog(@"Path parameter already encoded. Building file URL without encoding it...");
fileURL = [NSURL URLWithString:path];
}
localFile = fileURL.path;
NSLog(@"looking for file at %@", fileURL);
NSFileManager *fm = [NSFileManager defaultManager];
if (![fm fileExistsAtPath:localFile]) {
NSDictionary *jsonObj = @{@"status" : @"9",
@"message" : @"File does not exist"};
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:jsonObj];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
return;
}
docController = [UIDocumentInteractionController interactionControllerWithURL:fileURL];
docController.delegate = self;
docController.UTI = uti;
//Opens the file preview
CGRect rect;
if ([command.arguments count] >= 4) {
NSArray *positionValues = [command.arguments objectAtIndex:3];
if (![positionValues isEqual:[NSNull null]] && [positionValues count] >= 2) {
rect = CGRectMake(0, 0, [[positionValues objectAtIndex:0] floatValue], [[positionValues objectAtIndex:1] floatValue]);
} else {
rect = CGRectMake(0, 0, 0, 0);
}
} else {
rect = CGRectMake(0, 0, cont.view.bounds.size.width, cont.view.bounds.size.height);
}
BOOL wasOpened = NO;
if (showPreview) {
wasOpened = [docController presentPreviewAnimated: NO];
} else {
CDVViewController* cont = self.cdvViewController;
wasOpened = [docController presentOpenInMenuFromRect:rect inView:cont.view animated:YES];
}
if (wasOpened) {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: @""];
} else {
NSDictionary *jsonObj = [ [NSDictionary alloc]
initWithObjectsAndKeys :
@"9", @"status",
@"Could not handle UTI", @"message",
nil
];
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:jsonObj];
[self.commandDelegate sendPluginResult:pluginResult callbackId:callbackId];
}
});
}
@end
@implementation FileOpener2 (UIDocumentInteractionControllerDelegate)
- (void)documentInteractionControllerDidDismissOpenInMenu:(UIDocumentInteractionController *)controller {
[self.commandDelegate sendPluginResult:pluginResult callbackId:callbackId];
}
- (void)documentInteractionControllerDidEndPreview:(UIDocumentInteractionController *)controller {
[self.commandDelegate sendPluginResult:pluginResult callbackId:callbackId];
}
- (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller {
UIViewController *presentingViewController = self.viewController;
if (presentingViewController.view.window != [UIApplication sharedApplication].keyWindow) {
presentingViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
}
while (presentingViewController.presentedViewController != nil && ![presentingViewController.presentedViewController isBeingDismissed]) {
presentingViewController = presentingViewController.presentedViewController;
}
return presentingViewController;
}
@end

View File

@@ -0,0 +1,97 @@
var cordova = require('cordova'),
fileOpener2 = require('./FileOpener2');
var schemes = [
{ protocol: 'ms-app', getFile: getFileFromApplicationUri },
{ protocol: 'cdvfile', getFile: getFileFromFileUri } //protocol cdvfile
]
function nthIndex(str, pat, n) {
var L = str.length, i = -1;
while (n-- && i++ < L) {
i = str.indexOf(pat, i);
if (i < 0) break;
}
return i;
}
function getFileFromApplicationUri(uri) {
/* bad path from a file entry due to the last '//'
example: ms-appdata:///local//path/to/file
*/
var index = nthIndex(uri, "//", 3);
var newUri = uri.substr(0, index) + uri.substr(index + 1);
var applicationUri = new Windows.Foundation.Uri(newUri);
return Windows.Storage.StorageFile.getFileFromApplicationUriAsync(applicationUri);
}
function getFileFromFileUri(uri) {
/* uri example:
cdvfile://localhost/persistent|temporary|another-fs-root/path/to/file
*/
var indexFrom = nthIndex(uri, "/", 3) + 1;
var indexTo = nthIndex(uri, "/", 4);
var whichFolder = uri.substring(indexFrom, indexTo);
var filePath = uri.substr(indexTo + 1);
var path = "\\" + filePath;
if (whichFolder == "persistent") {
path = Windows.Storage.ApplicationData.current.localFolder.path + path;
}
else { //temporary, note: no roaming management
path = Windows.Storage.ApplicationData.current.temporaryFolder.path + path;
}
return getFileFromNativePath(path);
}
function getFileFromNativePath(path) {
var nativePath = path.split("/").join("\\");
return Windows.Storage.StorageFile.getFileFromPathAsync(nativePath);
}
function getFileLoaderForScheme(path) {
var fileLoader = getFileFromNativePath;
schemes.some(function (scheme) {
return path.indexOf(scheme.protocol) === 0 ? ((fileLoader = scheme.getFile), true) : false;
});
return fileLoader;
}
module.exports = {
open: function (successCallback, errorCallback, args) {
var path = args[0];
var getFile = getFileLoaderForScheme(path);
getFile(path).then(function (file) {
var options = new Windows.System.LauncherOptions();
try{
Windows.System.Launcher.launchFileAsync(file, options).then(function (success) {
successCallback();
}, function (error) {
errorCallback(error);
});
}catch(error){
errorCallback(error);
}
}, function (error) {
console.log("Error while opening the file: "+error);
errorCallback(error);
});
}
};
require("cordova/exec/proxy").add("FileOpener2", module.exports);

View File

@@ -0,0 +1,26 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
module.exports = function () {
// window.webkitRequestFileSystem and window.webkitResolveLocalFileSystemURL are available only in Chrome and
// possibly a good flag to indicate that we're running in Chrome
return window.webkitRequestFileSystem && window.webkitResolveLocalFileSystemURL;
};

View File

@@ -0,0 +1,51 @@
/*jslint browser: true, devel: true, node: true, sloppy: true, plusplus: true*/
/*global require, cordova */
/*
The MIT License (MIT)
Copyright (c) 2013 pwlin - pwlin05@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var exec = require('cordova/exec');
function FileOpener2() {}
FileOpener2.prototype.open = function (fileName, contentType, callbackContext) {
contentType = contentType || '';
callbackContext = callbackContext || {};
exec(callbackContext.success || null, callbackContext.error || null, 'FileOpener2', 'open', [fileName, contentType]);
};
FileOpener2.prototype.showOpenWithDialog = function (fileName, contentType, callbackContext) {
contentType = contentType || '';
callbackContext = callbackContext || {};
exec(callbackContext.success || null, callbackContext.error || null, 'FileOpener2', 'open', [fileName, contentType, false, callbackContext.position || [0, 0]]);
};
FileOpener2.prototype.uninstall = function (packageId, callbackContext) {
callbackContext = callbackContext || {};
exec(callbackContext.success || null, callbackContext.error || null, 'FileOpener2', 'uninstall', [packageId]);
};
FileOpener2.prototype.appIsInstalled = function (packageId, callbackContext) {
callbackContext = callbackContext || {};
exec(callbackContext.success || null, callbackContext.error || null, 'FileOpener2', 'appIsInstalled', [packageId]);
};
module.exports = new FileOpener2();

View File

@@ -0,0 +1,28 @@
# appveyor file
# http://www.appveyor.com/docs/appveyor-yml
max_jobs: 1
shallow_clone: true
init:
- git config --global core.autocrlf true
image:
- Visual Studio 2017
environment:
nodejs_version: "4"
matrix:
- PLATFORM: windows-10-store
install:
- npm cache clean -f
- node --version
- npm install -g cordova-paramedic@https://github.com/apache/cordova-paramedic.git
- npm install -g cordova
build: off
test_script:
- cordova-paramedic --config pr\%PLATFORM% --plugin . --justBuild

View File

@@ -0,0 +1,22 @@
<!--
Please make sure the checklist boxes are all checked before submitting the PR. The checklist
is intended as a quick reference, for complete details please see our Contributor Guidelines:
http://cordova.apache.org/contribute/contribute_guidelines.html
Thanks!
-->
### Platforms affected
### What does this PR do?
### What testing has been done on this change?
### Checklist
- [ ] [Reported an issue](http://cordova.apache.org/contribute/issues.html) in the JIRA database
- [ ] Commit message follows the format: "CB-3232: (android) Fix bug with resolving file paths", where CB-xxxx is the JIRA ID & "android" is the platform affected.
- [ ] Added automated test coverage as appropriate for this change.

View File

@@ -0,0 +1,23 @@
{
"disallowMixedSpacesAndTabs": true,
"disallowTrailingWhitespace": true,
"validateIndentation": 4,
"requireLineFeedAtFileEnd": true,
"disallowSpaceAfterPrefixUnaryOperators": true,
"disallowSpaceBeforePostfixUnaryOperators": true,
"requireSpaceAfterLineComment": true,
"requireCapitalizedConstructors": true,
"disallowSpacesInNamedFunctionExpression": {
"beforeOpeningRoundBrace": true
},
"requireSpaceAfterKeywords": [
"if",
"else",
"for",
"while",
"do"
]
}

View File

@@ -0,0 +1,19 @@
{
"browser": true
, "devel": true
, "bitwise": true
, "undef": true
, "trailing": true
, "quotmark": false
, "indent": 4
, "unused": "vars"
, "latedef": "nofunc"
, "globals": {
"module": false,
"exports": false,
"require": false,
"FileTransferError": true,
"FileUploadResult": true,
"resolveLocalFileSystemURI": false
}
}

View File

@@ -0,0 +1,15 @@
#If ignorance is bliss, then somebody knock the smile off my face
*.csproj.user
*.suo
*.cache
Thumbs.db
*.DS_Store
*.bak
*.cache
*.log
*.swp
*.user
node_modules

View File

@@ -0,0 +1,71 @@
sudo: false
addons:
jwt:
secure: hXzjjCvaueH+0Mm66ym6nE3jncWjjGIXIqhrqQUpWI7WTzcEXPFzA2ljyMNcMbPCcXSpq7u3oEBgZl5VUd+PWfBWKCFbcTu0KP1KP7s2GeTp/xvWWoxxRYfU8FhUh5ei/opSBBDhT9zqt85efHt09aqAzos0THLpYPGxFPknzY0=
env:
global:
- SAUCE_USERNAME=snay
- TRAVIS_NODE_VERSION="4.2"
matrix:
include:
- env: PLATFORM=ios-9.3
os: osx
osx_image: xcode7.3
language: node_js
node_js: '4.2'
- env: PLATFORM=ios-10.0
os: osx
osx_image: xcode7.3
language: node_js
node_js: '4.2'
- env: PLATFORM=android-4.4
os: linux
language: android
jdk: oraclejdk8
android:
components:
- tools
- build-tools-26.0.2
- env: PLATFORM=android-5.1
os: linux
language: android
jdk: oraclejdk8
android:
components:
- tools
- build-tools-26.0.2
- env: PLATFORM=android-6.0
os: linux
language: android
jdk: oraclejdk8
android:
components:
- tools
- build-tools-26.0.2
- env: PLATFORM=android-7.0
os: linux
language: android
jdk: oraclejdk8
android:
components:
- tools
- build-tools-26.0.2
before_install:
- rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm
&& git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm
install $TRAVIS_NODE_VERSION
- node --version
- if [[ "$PLATFORM" =~ android ]]; then gradle --version; fi
- if [[ "$PLATFORM" =~ ios ]]; then npm install -g ios-deploy; fi
- if [[ "$PLATFORM" =~ android ]]; then echo y | android update sdk -u --filter android-22,android-23,android-24,android-25,android-26;
fi
- git clone https://github.com/apache/cordova-paramedic /tmp/paramedic && pushd /tmp/paramedic
&& npm install && popd
- npm install -g cordova
install:
- npm install
script:
- npm test
- node /tmp/paramedic/main.js --config pr/$PLATFORM --plugin $(pwd) --shouldUseSauce
--fileTransferServer http://sheltered-retreat-43956.herokuapp.com
--buildName travis-plugin-file-transfer-$TRAVIS_JOB_NUMBER

View File

@@ -0,0 +1,37 @@
<!--
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
-->
# Contributing to Apache Cordova
Anyone can contribute to Cordova. And we need your contributions.
There are multiple ways to contribute: report bugs, improve the docs, and
contribute code.
For instructions on this, start with the
[contribution overview](http://cordova.apache.org/contribute/).
The details are explained there, but the important items are:
- Sign and submit an Apache ICLA (Contributor License Agreement).
- Have a Jira issue open that corresponds to your contribution.
- Run the tests so your patch doesn't break existing functionality.
We look forward to your contributions!

View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,8 @@
Apache Cordova
Copyright 2012 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
This product includes a copy of OkHttp from:
https://github.com/square/okhttp

View File

@@ -0,0 +1,598 @@
---
title: File Transfer
description: Upload and download files.
---
<!--
# license: Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-->
|AppVeyor|Travis CI|
|:-:|:-:|
|[![Build status](https://ci.appveyor.com/api/projects/status/github/apache/cordova-plugin-file-transfer?branch=master)](https://ci.appveyor.com/project/ApacheSoftwareFoundation/cordova-plugin-file-transfer)|[![Build Status](https://travis-ci.org/apache/cordova-plugin-file-transfer.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-file-transfer)|
# cordova-plugin-file-transfer
This plugin allows you to upload and download files.
This plugin defines global `FileTransfer`, `FileUploadOptions` constructors. Although in the global scope, they are not available until after the `deviceready` event.
```js
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
console.log(FileTransfer);
}
```
> To get a few ideas, check out the [sample](#sample) at the bottom of this page.
Report issues with this plugin on the [Apache Cordova issue tracker](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20%28Open%2C%20%22In%20Progress%22%2C%20Reopened%29%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22Plugin%20File%20Transfer%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC)
## Deprecated
With the new features introduced in [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest), this plugin is not needed any more. Migrating from this plugin to using the new features of [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest), is explained in this [Cordova blog post](https://cordova.apache.org/blog/2017/10/18/from-filetransfer-to-xhr2.html).
## Installation
```bash
cordova plugin add cordova-plugin-file-transfer
```
## Supported Platforms
- Amazon Fire OS
- Android
- BlackBerry 10
- Browser
- Firefox OS**
- iOS
- Windows Phone 7 and 8*
- Windows
\* _Do not support `onprogress` nor `abort()`_
\** _Do not support `onprogress`_
# FileTransfer
The `FileTransfer` object provides a way to upload files using an HTTP
multi-part POST or PUT request, and to download files.
## Properties
- __onprogress__: Called with a `ProgressEvent` whenever a new chunk of data is transferred. _(Function)_
## Methods
- __upload__: Sends a file to a server.
- __download__: Downloads a file from server.
- __abort__: Aborts an in-progress transfer.
## upload
__Parameters__:
- __fileURL__: Filesystem URL representing the file on the device or a [data URI](https://en.wikipedia.org/wiki/Data_URI_scheme). For backwards compatibility, this can also be the full path of the file on the device. (See [Backwards Compatibility Notes](#backwards-compatibility-notes) below)
- __server__: URL of the server to receive the file, as encoded by `encodeURI()`.
- __successCallback__: A callback that is passed a `FileUploadResult` object. _(Function)_
- __errorCallback__: A callback that executes if an error occurs retrieving the `FileUploadResult`. Invoked with a `FileTransferError` object. _(Function)_
- __options__: Optional parameters _(Object)_. Valid keys:
- __fileKey__: The name of the form element. Defaults to `file`. (DOMString)
- __fileName__: The file name to use when saving the file on the server. Defaults to `image.jpg`. (DOMString)
- __httpMethod__: The HTTP method to use - either `PUT` or `POST`. Defaults to `POST`. (DOMString)
- __mimeType__: The mime type of the data to upload. Defaults to `image/jpeg`. (DOMString)
- __params__: A set of optional key/value pairs to pass in the HTTP request. (Object, key/value - DOMString)
- __chunkedMode__: Whether to upload the data in chunked streaming mode. Defaults to `true`. (Boolean)
- __headers__: A map of header name/header values. Use a hash to specify one or more than one value. On iOS, FireOS, and Android, if a header named Content-Type is present, multipart form data will NOT be used. (Object)
- __trustAllHosts__: Optional parameter, defaults to `false`. If set to `true`, it accepts all security certificates. Not recommended for production use. Supported on iOS. _(boolean)_
### Example
```js
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function (r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
var fail = function (error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
options.mimeType = "text/plain";
var params = {};
params.value1 = "test";
params.value2 = "param";
options.params = params;
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
```
### Example with Upload Headers and Progress Events (Android and iOS only)
```js
function win(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
function fail(error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var uri = encodeURI("http://some.server.com/upload.php");
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1);
options.mimeType="text/plain";
var headers={'headerParam':'headerValue', 'headerParam2':'headerValue2'};
options.headers = headers;
var ft = new FileTransfer();
ft.onprogress = function(progressEvent) {
if (progressEvent.lengthComputable) {
loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
} else {
loadingStatus.increment();
}
};
ft.upload(fileURL, uri, win, fail, options);
```
## FileUploadResult
A `FileUploadResult` object is passed to the success callback of the
`FileTransfer` object's `upload()` method.
### Properties
- __bytesSent__: The number of bytes sent to the server as part of the upload. (long)
- __responseCode__: The HTTP response code returned by the server. (long)
- __response__: The HTTP response returned by the server. (DOMString)
- __headers__: The HTTP response headers by the server. (Object)
- Currently supported on iOS only.
### iOS Quirks
- Does not support `responseCode` or `bytesSent`.
- Does not support uploads of an empty file with __chunkedMode=true__ and `multipartMode=false`.
### Browser Quirks
- __withCredentials__: _boolean_ that tells the browser to set the withCredentials flag on the XMLHttpRequest
### Windows Quirks
- An option parameter with empty/null value is excluded in the upload operation due to the Windows API design.
- __chunkedMode__ is not supported and all uploads are set to non-chunked mode.
## download
__Parameters__:
- __source__: URL of the server to download the file, as encoded by `encodeURI()`.
- __target__: Filesystem url representing the file on the device. For backwards compatibility, this can also be the full path of the file on the device. (See [Backwards Compatibility Notes](#backwards-compatibility-notes) below)
- __successCallback__: A callback that is passed a `FileEntry` object. _(Function)_
- __errorCallback__: A callback that executes if an error occurs when retrieving the `FileEntry`. Invoked with a `FileTransferError` object. _(Function)_
- __trustAllHosts__: Optional parameter, defaults to `false`. If set to `true`, it accepts all security certificates. Not recommended for production use. Supported on iOS. _(boolean)_
- __options__: Optional parameters, currently only supports headers (such as Authorization (Basic Authentication), etc).
### Example
```js
// !! Assumes variable fileURL contains a valid URL to a path on the device,
// for example, cdvfile://localhost/persistent/path/to/downloads/
var fileTransfer = new FileTransfer();
var uri = encodeURI("http://some.server.com/download.php");
fileTransfer.download(
uri,
fileURL,
function(entry) {
console.log("download complete: " + entry.toURL());
},
function(error) {
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("download error code" + error.code);
},
false,
{
headers: {
"Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
}
}
);
```
### WP8 Quirks
- Download requests is being cached by native implementation. To avoid caching, pass `if-Modified-Since` header to download method.
### Browser Quirks
- __withCredentials__: _boolean_ that tells the browser to set the withCredentials flag on the XMLHttpRequest
## abort
Aborts an in-progress transfer. The onerror callback is passed a FileTransferError object which has an error code of `FileTransferError.ABORT_ERR`.
### Example
```js
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function(r) {
console.log("Should not be called.");
}
var fail = function(error) {
// error.code == FileTransferError.ABORT_ERR
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName="myphoto.jpg";
options.mimeType="image/jpeg";
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
ft.abort();
```
## FileTransferError
A `FileTransferError` object is passed to an error callback when an error occurs.
### Properties
- __code__: One of the predefined error codes listed below. (Number)
- __source__: URL to the source. (String)
- __target__: URL to the target. (String)
- __http_status__: HTTP status code. This attribute is only available when a response code is received from the HTTP connection. (Number)
- __body__ Response body. This attribute is only available when a response is received from the HTTP connection. (String)
- __exception__: Either e.getMessage or e.toString (String)
### iOS Quirks
__exception__ is never defined.
### Constants
- 1 = `FileTransferError.FILE_NOT_FOUND_ERR`
- 2 = `FileTransferError.INVALID_URL_ERR`
- 3 = `FileTransferError.CONNECTION_ERR`
- 4 = `FileTransferError.ABORT_ERR`
- 5 = `FileTransferError.NOT_MODIFIED_ERR`
## Windows Quirks
- The plugin implementation is based on [BackgroundDownloader](https://msdn.microsoft.com/en-us/library/windows/apps/windows.networking.backgroundtransfer.backgrounddownloader.aspx)/[BackgroundUploader](https://msdn.microsoft.com/en-us/library/windows/apps/windows.networking.backgroundtransfer.backgrounduploader.aspx), which entails the latency issues on Windows devices (creation/starting of an operation can take up to a few seconds). You can use XHR or [HttpClient](https://msdn.microsoft.com/en-us/library/windows/apps/windows.web.http.httpclient.aspx) as a quicker alternative for small downloads.
## Backwards Compatibility Notes
Previous versions of this plugin would only accept device-absolute-file-paths as the source for uploads, or as the target for downloads. These paths would typically be of the form:
/var/mobile/Applications/<application UUID>/Documents/path/to/file (iOS)
/storage/emulated/0/path/to/file (Android)
For backwards compatibility, these paths are still accepted, and if your application has recorded paths like these in persistent storage, then they can continue to be used.
These paths were previously exposed in the `fullPath` property of `FileEntry` and `DirectoryEntry` objects returned by the File plugin. New versions of the File plugin however, no longer expose these paths to JavaScript.
If you are upgrading to a new (1.0.0 or newer) version of File, and you have previously been using `entry.fullPath` as arguments to `download()` or `upload()`, then you will need to change your code to use filesystem URLs instead.
`FileEntry.toURL()` and `DirectoryEntry.toURL()` return a filesystem URL of the form:
cdvfile://localhost/persistent/path/to/file
which can be used in place of the absolute file path in both `download()` and `upload()` methods.
## Sample: Download and Upload Files <a name="sample"></a>
Use the File-Transfer plugin to upload and download files. In these examples, we demonstrate several tasks like:
* [Downloading a binary file to the application cache](#binaryFile)
* [Uploading a file created in your application's root](#uploadFile)
* [Downloading the uploaded file](#downloadFile)
## Download a Binary File to the application cache <a name="binaryFile"></a>
Use the File plugin with the File-Transfer plugin to provide a target for the files that you download (the target must be a FileEntry object). Before you download the file, create a DirectoryEntry object by using `resolveLocalFileSystemURL` and calling `fs.root` in the success callback. Use the `getFile` method of DirectoryEntry to create the target file.
```js
window.requestFileSystem(window.TEMPORARY, 5 * 1024 * 1024, function (fs) {
console.log('file system open: ' + fs.name);
// Make sure you add the domain name to the Content-Security-Policy <meta> element.
var url = 'http://cordova.apache.org/static/img/cordova_bot.png';
// Parameters passed to getFile create a new file or return the file if it already exists.
fs.root.getFile('downloaded-image.png', { create: true, exclusive: false }, function (fileEntry) {
download(fileEntry, url, true);
}, onErrorCreateFile);
}, onErrorLoadFs);
```
>*Note* For persistent storage, pass LocalFileSystem.PERSISTENT to requestFileSystem.
When you have the FileEntry object, download the file using the `download` method of the FileTransfer object. The 3rd argument to the `download` function of FileTransfer is the success callback, which you can use to call the app's `readBinaryFile` function. In this code example, the `entry` variable is a new FileEntry object that receives the result of the download operation.
```js
function download(fileEntry, uri, readBinaryData) {
var fileTransfer = new FileTransfer();
var fileURL = fileEntry.toURL();
fileTransfer.download(
uri,
fileURL,
function (entry) {
console.log("Successful download...");
console.log("download complete: " + entry.toURL());
if (readBinaryData) {
// Read the file...
readBinaryFile(entry);
}
else {
// Or just display it.
displayImageByFileURL(entry);
}
},
function (error) {
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("upload error code" + error.code);
},
null, // or, pass false
{
//headers: {
// "Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
//}
}
);
}
```
If you just need to display the image, take the FileEntry to call its toURL() function.
```js
function displayImageByFileURL(fileEntry) {
var elem = document.getElementById('imageElement');
elem.src = fileEntry.toURL();
}
```
Depending on your app requirements, you may want to read the file. To support operations with binary files, FileReader supports two methods, `readAsBinaryString` and `readAsArrayBuffer`. In this example, use `readAsArrayBuffer` and pass the FileEntry object to the method. Once you read the file successfully, construct a Blob object using the result of the read.
```js
function readBinaryFile(fileEntry) {
fileEntry.file(function (file) {
var reader = new FileReader();
reader.onloadend = function() {
console.log("Successful file read: " + this.result);
// displayFileData(fileEntry.fullPath + ": " + this.result);
var blob = new Blob([new Uint8Array(this.result)], { type: "image/png" });
displayImage(blob);
};
reader.readAsArrayBuffer(file);
}, onErrorReadFile);
}
```
Once you read the file successfully, you can create a DOM URL string using `createObjectURL`, and then display the image.
```js
function displayImage(blob) {
// Note: Use window.URL.revokeObjectURL when finished with image.
var objURL = window.URL.createObjectURL(blob);
// Displays image if result is a valid DOM string for an image.
var elem = document.getElementById('imageElement');
elem.src = objURL;
}
```
As you saw previously, you can call FileEntry.toURL() instead to just display the downloaded image (skip the file read).
## Upload a File <a name="uploadFile"></a>
When you upload a File using the File-Transfer plugin, use the File plugin to provide files for upload (again, they must be FileEntry objects). Before you can upload anything, create a file for upload using the `getFile` method of DirectoryEntry. In this example, create the file in the application's cache (fs.root). Then call the app's writeFile function so you have some content to upload.
```js
function onUploadFile() {
window.requestFileSystem(window.TEMPORARY, 5 * 1024 * 1024, function (fs) {
console.log('file system open: ' + fs.name);
var fileName = "uploadSource.txt";
var dirEntry = fs.root;
dirEntry.getFile(fileName, { create: true, exclusive: false }, function (fileEntry) {
// Write something to the file before uploading it.
writeFile(fileEntry);
}, onErrorCreateFile);
}, onErrorLoadFs);
}
```
In this example, create some simple content, and then call the app's upload function.
```js
function writeFile(fileEntry, dataObj) {
// Create a FileWriter object for our FileEntry (log.txt).
fileEntry.createWriter(function (fileWriter) {
fileWriter.onwriteend = function () {
console.log("Successful file write...");
upload(fileEntry);
};
fileWriter.onerror = function (e) {
console.log("Failed file write: " + e.toString());
};
if (!dataObj) {
dataObj = new Blob(['file data to upload'], { type: 'text/plain' });
}
fileWriter.write(dataObj);
});
}
```
Forward the FileEntry object to the upload function. To perform the actual upload, use the upload function of the FileTransfer object.
```js
function upload(fileEntry) {
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
var fileURL = fileEntry.toURL();
var success = function (r) {
console.log("Successful upload...");
console.log("Code = " + r.responseCode);
// displayFileData(fileEntry.fullPath + " (content uploaded to server)");
}
var fail = function (error) {
alert("An error has occurred: Code = " + error.code);
}
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
options.mimeType = "text/plain";
var params = {};
params.value1 = "test";
params.value2 = "param";
options.params = params;
var ft = new FileTransfer();
// SERVER must be a URL that can handle the request, like
// http://some.server.com/upload.php
ft.upload(fileURL, encodeURI(SERVER), success, fail, options);
};
```
## Download the uploaded file <a name="downloadFile"></a>
To download the image you just uploaded, you will need a valid URL that can handle the request, for example, http://some.server.com/download.php. Again, the success handler for the FileTransfer.download method receives a FileEntry object. The main difference here from previous examples is that we call FileReader.readAsText to read the result of the download operation, because we uploaded a file with text content.
```js
function download(fileEntry, uri) {
var fileTransfer = new FileTransfer();
var fileURL = fileEntry.toURL();
fileTransfer.download(
uri,
fileURL,
function (entry) {
console.log("Successful download...");
console.log("download complete: " + entry.toURL());
readFile(entry);
},
function (error) {
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("upload error code" + error.code);
},
null, // or, pass false
{
//headers: {
// "Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
//}
}
);
}
```
In the readFile function, call the `readAsText` method of the FileReader object.
```js
function readFile(fileEntry) {
fileEntry.file(function (file) {
var reader = new FileReader();
reader.onloadend = function () {
console.log("Successful file read: " + this.result);
// displayFileData(fileEntry.fullPath + ": " + this.result);
};
reader.readAsText(file);
}, onErrorReadFile);
}
```

View File

@@ -0,0 +1,319 @@
<!--
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
-->
# Release Notes
### 1.7.1 (Jan 24, 2018)
* [CB-13749](https://issues.apache.org/jira/browse/CB-13749) Add build-tools-26.0.2 to travis
### 1.7.0 (Nov 06, 2017)
* Updated `README` with Deprecated Status
* [CB-13472](https://issues.apache.org/jira/browse/CB-13472) (CI) Fixed Travis **Android** builds again
* [CB-12809](https://issues.apache.org/jira/browse/CB-12809) Google Play Blocker: Unsafe SSL TrustManager Defined
* [CB-7995](https://issues.apache.org/jira/browse/CB-7995) document that `FileTransferError.exception` on **iOS** is never defined.
* [CB-13000](https://issues.apache.org/jira/browse/CB-13000) (CI) Speed up **Android** builds
* [CB-12847](https://issues.apache.org/jira/browse/CB-12847) added `bugs` entry to `package.json`.
### 1.6.3 (Apr 27, 2017)
* [CB-12685](https://issues.apache.org/jira/browse/CB-12685) added `package.json` to tests folder
* [CB-10696](https://issues.apache.org/jira/browse/CB-10696) **iOS**: Encode target path with spaces
### 1.6.2 (Feb 28, 2017)
* [CB-12353](https://issues.apache.org/jira/browse/CB-12353) Corrected merges usage in `plugin.xml`
* [CB-12369](https://issues.apache.org/jira/browse/CB-12369) Add plugin typings from `DefinitelyTyped`
* [CB-12363](https://issues.apache.org/jira/browse/CB-12363) Added build badges for **iOS 9.3** and **iOS 10.0**
* [CB-12230](https://issues.apache.org/jira/browse/CB-12230) Removed **Windows 8.1** build badges
### 1.6.1 (Dec 07, 2016)
* [CB-12224](https://issues.apache.org/jira/browse/CB-12224) Updated version and RELEASENOTES.md for release 1.6.1
* [CB-12154](https://issues.apache.org/jira/browse/CB-12154) file-transfer progressEvent.total = -1 on iOS
* [CB-10974](https://issues.apache.org/jira/browse/CB-10974) Cordova file transfer Content-Length header problem
* Don't crash on low memory devices
* [CB-12100](https://issues.apache.org/jira/browse/CB-12100) (ios) Fixed test plugin install at platform add on cordova@6.3.1
* [CB-11959](https://issues.apache.org/jira/browse/CB-11959) Fixed the jshint issues
* [CB-11959](https://issues.apache.org/jira/browse/CB-11959) Increased the array length for ios and winstore even more
* [CB-11959](https://issues.apache.org/jira/browse/CB-11959) Fixed filetransfer.spec.21 test failure on iOS and Windows Store when using local server
* [CB-11917](https://issues.apache.org/jira/browse/CB-11917) - Remove pull request template checklist item: "iCLA has been submitted…"
* [CB-11926](https://issues.apache.org/jira/browse/CB-11926) Tests can use local server
* [CB-11832](https://issues.apache.org/jira/browse/CB-11832) Incremented plugin version.
### 1.6.0 (Sep 08, 2016)
* [CB-11795](https://issues.apache.org/jira/browse/CB-11795) Add 'protective' entry to cordovaDependencies
* [CB-9022](https://issues.apache.org/jira/browse/CB-9022) Fix exception thrown by call to `remapApi` on main thread
* Plugin uses `Android Log class` and not `Cordova LOG class`
* [CB-9022](https://issues.apache.org/jira/browse/CB-9022) Resolve source URI on background thread
* [CB-11316](https://issues.apache.org/jira/browse/CB-11316) **windows**: Added `content-type` for files
* Close invalid PRs
* [CB-11497](https://issues.apache.org/jira/browse/CB-11497) Use `cordova-vm` for testing 304 errors
* Add badges for paramedic builds on Jenkins
* documentation with a wrong log message in `fileTransfer.download` function
* added link to sample
* Add pull request template.
* [CB-10974](https://issues.apache.org/jira/browse/CB-10974) Cordova file transfer `Content-Length` header problem
* Add fenced code blocks to help code formatting
* [CB-11165](https://issues.apache.org/jira/browse/CB-11165) removed peer dependency
* [CB-11003](https://issues.apache.org/jira/browse/CB-11003) Adding sample section to documentation.
* [CB-10996](https://issues.apache.org/jira/browse/CB-10996) Adding front matter to README.md
### 1.5.1 (Apr 15, 2016)
* [CB-10536](https://issues.apache.org/jira/browse/CB-10536) Removing flaky test assertions about abort callback latency
* Removing the expectation in `spec.34` for the transfer method to be called.
* [CB-10978](https://issues.apache.org/jira/browse/CB-10978) Fix `file-transfer.tests` JSHint issues
* [CB-10782](https://issues.apache.org/jira/browse/CB-10782) Occasional failure in file transfer tests causing mobilespec crash
* [CB-10771](https://issues.apache.org/jira/browse/CB-10771) Fixing failure when empty string passed as a value for option parameter in upload function
* [CB-10636](https://issues.apache.org/jira/browse/CB-10636) Add `JSHint` for plugins
### 1.5.0 (Jan 15, 2016)
* [CB-10208](https://issues.apache.org/jira/browse/CB-10208) Fix `file-transfer` multipart form data upload format on **Windows**
* [CB-9837](https://issues.apache.org/jira/browse/CB-9837) Add data `URI` support to `file-transfer` upload on **iOS**
* [CB-9600](https://issues.apache.org/jira/browse/CB-9600) `FileUploadOptions` params not posted on **iOS**
* [CB-9840](https://issues.apache.org/jira/browse/CB-9840) Fallback `file-transfer` `uploadResponse` encoding to `latin1` in case not encoded with `UTF-8` on **iOS**
* [CB-9840](https://issues.apache.org/jira/browse/CB-9840) Fallback `file-transfer` upload/download response encoding to `latin1` in case not encoded with `UTF-8` on **iOS**
* [CB-8641](https://issues.apache.org/jira/browse/CB-8641) **Windows Phone 8.1** Some `file-transfer` plugin tests occasionally fail in `mobilespec`
* Adding linting and fixing linter warnings. Reducing timeouts to 7 seconds.
* [CB-10100](https://issues.apache.org/jira/browse/CB-10100) updated file dependency to not grab new majors
* [CB-7006](https://issues.apache.org/jira/browse/CB-7006) Empty file is created on file transfer if server response is 304
* [CB-10098](https://issues.apache.org/jira/browse/CB-10098) `filetransfer.spec.33` is faulty
* [CB-9969](https://issues.apache.org/jira/browse/CB-9969) Filetransfer upload error deletes original file
* [CB-10088](https://issues.apache.org/jira/browse/CB-10088) `filetransfer spec.10` and `spec.11` test is faulty
* [CB-9969](https://issues.apache.org/jira/browse/CB-9969) Filetransfer upload error deletes original file
* [CB-10086](https://issues.apache.org/jira/browse/CB-10086) There are two `spec.31` tests for `file-transfer` tests
* [CB-10037](https://issues.apache.org/jira/browse/CB-10037) Add progress indicator to file-transfer manual tests
* [CB-9563](https://issues.apache.org/jira/browse/CB-9563) Mulptipart form data is used even a header named `Content-Type` is present
* [CB-8863](https://issues.apache.org/jira/browse/CB-8863) fix block usage of self
### 1.4.0 (Nov 18, 2015)
* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated `RELEASENOTES` to be newest to oldest
* [CB-9879](https://issues.apache.org/jira/browse/CB-9879) `getCookie`s can cause unhandled `NullPointerException`
* [CB-6928](https://issues.apache.org/jira/browse/CB-6928) Wrong behaviour transferring cacheable content
* [CB-51](https://issues.apache.org/jira/browse/CB-51) FileTransfer - Support `PUT` Method
* [CB-9906](https://issues.apache.org/jira/browse/CB-9906) cleanup duplicate code, removed 2nd `isWP8` declaration.
* [CB-9950](https://issues.apache.org/jira/browse/CB-9950) Unpend Filetransfer spec.27 on **wp8** as custom headers are now supported
* [CB-9843](https://issues.apache.org/jira/browse/CB-9843) Added **wp8** quirk to test spec 12
* Fixing contribute link.
* [CB-8431](https://issues.apache.org/jira/browse/CB-8431) File Transfer tests crash on **Android Lolipop**
* [CB-9790](https://issues.apache.org/jira/browse/CB-9790) Align `FileUploadOptions` `fileName` and `mimeType` default parameter values to the docs on **iOS**
* [CB-9385](https://issues.apache.org/jira/browse/CB-9385) Return `FILE_NOT_FOUND_ERR` when receiving `404` code on **iOS**
* [CB-9791](https://issues.apache.org/jira/browse/CB-9791) Decreased download and upload tests timeout
### 1.3.0 (Sep 18, 2015)
* Found issue where : is accepted as a valid header, this is obviously wrong
* [CB-9562](https://issues.apache.org/jira/browse/CB-9562) Fixed incorrect headers handling on Android
* Fixing headers so they don't accept non-ASCII
* updated tests to use cordova apache vm
* [CB-9493](https://issues.apache.org/jira/browse/CB-9493) Fix file paths in file-transfer manual tests
* [CB-8816](https://issues.apache.org/jira/browse/CB-8816) Add cdvfile:// support on windows
* [CB-9376](https://issues.apache.org/jira/browse/CB-9376) Fix FileTransfer plugin manual tests issue - 'undefined' in paths
### 1.2.1 (Jul 7, 2015)
* [CB-9275](https://issues.apache.org/jira/browse/CB-9275) [WP8] Fix build failure on WP8 by using reflection to detect presence of JSON.NET based serialization
* Updated code per code review.
* Updated documentation for browser
* Added option to allow for passing cookies automatically in the browser
### 1.2.0 (Jun 17, 2015)
* [CB-9128](https://issues.apache.org/jira/browse/CB-9128) cordova-plugin-file-transfer documentation translation: cordova-plugin-file-transfer
* [CB-6503](https://issues.apache.org/jira/browse/CB-6503): Null pointer check for headers in upload (This closes #27)
* [CB-6503](https://issues.apache.org/jira/browse/CB-6503): Allow payload content-types other than multipart/form-data to be used for upload
* Fix NoSuchMethodException looking up cookies.
* fix npm md issue
* [CB-8951](https://issues.apache.org/jira/browse/CB-8951) (wp8) Handle exceptions in download() and upload() again
* [wp8] Relaxed engine version requirement, using reflection to see if methods are available
* Check for the existence of Json.net assembly to determin how we deserialize our headers.
* relax engine requirement to allow -dev versions
* Remove verbose console log messages
* fix bad commit (mine) for cordova-wp8@4.0.0 engine req
* bump required cordova-wp8 version to 4.0.0
* This closes #80, This closes #12
* fix failing test resulting from overlapping async calls
* [CB-8721](https://issues.apache.org/jira/browse/CB-8721) Fixes incorrect headers and upload params parsing on wp8
* Replace all slashes in windows path
### 1.1.0 (May 06, 2015)
* [CB-8951](https://issues.apache.org/jira/browse/CB-8951) Fixed crash related to headers parsing on **wp8**
* [CB-8933](https://issues.apache.org/jira/browse/CB-8933) Increased download and upload test timeout
* [CB-6313](https://issues.apache.org/jira/browse/CB-6313) **wp8**: Extra boundary in upload
* [CB-8761](https://issues.apache.org/jira/browse/CB-8761) **wp8**: Copy cookies from WebBrowser
### 1.0.0 (Apr 15, 2015)
* [CB-8746](https://issues.apache.org/jira/browse/CB-8746) bumped version of file dependency
* [CB-8746](https://issues.apache.org/jira/browse/CB-8746) gave plugin major version bump
* [CB-8641](https://issues.apache.org/jira/browse/CB-8641) Fixed tests to pass on windows and wp8
* [CB-8583](https://issues.apache.org/jira/browse/CB-8583) Forces download to overwrite existing target file
* [CB-8589](https://issues.apache.org/jira/browse/CB-8589) Fixes upload failure when server's response doesn't contain any data
* [CB-8747](https://issues.apache.org/jira/browse/CB-8747) updated dependency, added peer dependency
* [CB-8683](https://issues.apache.org/jira/browse/CB-8683) changed plugin-id to pacakge-name
* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) properly updated translated docs to use new id
* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) updated translated docs to use new id
* Use TRAVIS_BUILD_DIR, install paramedic by npm
* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) Updated Readme
* [CB-8654](https://issues.apache.org/jira/browse/CB-8654) Note WP8 download requests caching in docs
* [CB-8590](https://issues.apache.org/jira/browse/CB-8590) (Windows) Fixed download.onprogress.lengthComputable
* [CB-8566](https://issues.apache.org/jira/browse/CB-8566) Integrate TravisCI
* [CB-8438](https://issues.apache.org/jira/browse/CB-8438) cordova-plugin-file-transfer documentation translation: cordova-plugin-file-transfer
* [CB-8538](https://issues.apache.org/jira/browse/CB-8538) Added package.json file
* [CB-8495](https://issues.apache.org/jira/browse/CB-8495) Fixed wp8 and wp81 test failures
* [CB-7957](https://issues.apache.org/jira/browse/CB-7957) Adds support for `browser` platform
* [CB-8429](https://issues.apache.org/jira/browse/CB-8429) Updated version and RELEASENOTES.md for release 0.5.0 (take 2)
* Fixes typo, introduced in https://github.com/apache/cordova-plugin-file-transfer/commit/bc43b46
* [CB-8407](https://issues.apache.org/jira/browse/CB-8407) Use File proxy to construct valid FileEntry for download success callback
* [CB-8407](https://issues.apache.org/jira/browse/CB-8407) Removes excess path to native path conversion in download method
* [CB-8429](https://issues.apache.org/jira/browse/CB-8429) Updated version and RELEASENOTES.md for release 0.5.0
* [CB-7957](https://issues.apache.org/jira/browse/CB-7957) Adds support for `browser` platform
* [CB-8095](https://issues.apache.org/jira/browse/CB-8095) Fixes JSHint and formatting issues
* [CB-8095](https://issues.apache.org/jira/browse/CB-8095) Updates tests and documentation
* [CB-8095](https://issues.apache.org/jira/browse/CB-8095) Rewrite upload method to support progress events properly
* android: Fix error reporting for unknown uri type on sourceUri instead of targetUri
### 0.5.0 (Feb 04, 2015)
* [CB-8407](https://issues.apache.org/jira/browse/CB-8407) windows: Fix download of `ms-appdata:///` URIs
* [CB-8095](https://issues.apache.org/jira/browse/CB-8095) windows: Rewrite upload method to support progress events properly
* [CB-5059](https://issues.apache.org/jira/browse/CB-5059) android: Add a CookieManager abstraction for pluggable webviews
* ios: Fix compile warning about implicity int conversion
* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) ios: Use argumentForIndex rather than NSArray extension
* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) ios: Use a local copy of DLog macro rather than CordovaLib version
* [CB-8296](https://issues.apache.org/jira/browse/CB-8296) ios: Fix crash when upload fails and file is not yet created (close #57)
* Document "body" property on FileTransferError
* [CB-7912](https://issues.apache.org/jira/browse/CB-7912) ios, android: Update to work with whitelist plugins in Cordova 4.x
* Error callback should always be called with the FileTransferError object, and not just the code
* windows: alias appData to Windows.Storage.ApplicationData.current
* [CB-8093](https://issues.apache.org/jira/browse/CB-8093) Fixes incorrect FileTransferError returned in case of download failure
### 0.4.8 (Dec 02, 2014)
* [CB-8021](https://issues.apache.org/jira/browse/CB-8021) - adds documentation for `httpMethod` to `doc/index.md`. However, translations still need to be addressed.
* [CB-7223](https://issues.apache.org/jira/browse/CB-7223) spec.27 marked pending for **wp8**
* [CB-6900](https://issues.apache.org/jira/browse/CB-6900) fixed `spec.7` for **wp8**
* [CB-7944](https://issues.apache.org/jira/browse/CB-7944) Pended unsupported auto tests for *Windows*
* [CB-7977](https://issues.apache.org/jira/browse/CB-7977) Mention `deviceready` in plugin docs
* [CB-7700](https://issues.apache.org/jira/browse/CB-7700) cordova-plugin-file-transfer documentation translation: cordova-plugin-file-transfer
### 0.4.7 (Oct 03, 2014)
* Construct proper FileEntry with nativeURL property set
* [CB-7532](https://issues.apache.org/jira/browse/CB-7532) Handle non-existent download dirs properly
* [CB-7529](https://issues.apache.org/jira/browse/CB-7529) Adds support for 'ms-appdata' URIs for windows
### 0.4.6 (Sep 17, 2014)
* [CB-7471](https://issues.apache.org/jira/browse/CB-7471) cordova-plugin-file-transfer documentation translation
* [CB-7249](https://issues.apache.org/jira/browse/CB-7249) cordova-plugin-file-transfer documentation translation
* [CB-7423](https://issues.apache.org/jira/browse/CB-7423) fix spec28,29 lastProgressEvent not visible to afterEach function
* Amazon related changes.
* Remove dupe file windows+windows8 both use the same one
* [CB-7316](https://issues.apache.org/jira/browse/CB-7316) Updates docs with actual information.
* [CB-7316](https://issues.apache.org/jira/browse/CB-7316) Adds support for Windows platform, moves \*Proxy files to proper directory.
* [CB-7316](https://issues.apache.org/jira/browse/CB-7316) Improves current specs compatibility:
* added documentation for new test
* [CB-6466](https://issues.apache.org/jira/browse/CB-6466) Fix failing test due to recent url change
* [CB-6466](https://issues.apache.org/jira/browse/CB-6466) created mobile-spec test
* Renamed test dir, added nested plugin.xml and test
* Fixed failing spec.19 on wp8
* added documentation to manual tests
* [CB-6961](https://issues.apache.org/jira/browse/CB-6961) port file-transfer tests to framework
### 0.4.5 (Aug 06, 2014)
* Upload parameters out of order
* **FirefoxOS** initial implementation
* [CB-6781](https://issues.apache.org/jira/browse/CB-6781): Expose FileTransferError.exception to application
* [CB-6928](https://issues.apache.org/jira/browse/CB-6928): Add new error code to documentation
* [CB-6928](https://issues.apache.org/jira/browse/CB-6928): Handle 304 status code
* [CB-6928](https://issues.apache.org/jira/browse/CB-6928): Open output stream only if it's necessary.
* [BlackBerry10] Minor doc correction
* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Updated translations for docs
* [Windows8] upload uses the provided fileName or the actual fileName
* [CB-2420](https://issues.apache.org/jira/browse/CB-2420) [Windows8] honor fileKey and param options. This closes #15
* [CB-6781](https://issues.apache.org/jira/browse/CB-6781): Update new docs to match AlexNennker's changes in PR30
* [CB-6781](https://issues.apache.org/jira/browse/CB-6781): Continue previous commit with one new instance (This closes #30)
* [CB-6781](https://issues.apache.org/jira/browse/CB-6781): add the exception text to the error object
* [CB-6890](https://issues.apache.org/jira/browse/CB-6890): Fix pluginManager access for 4.0.x branch
### 0.4.4 (Jun 05, 2014)
* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Spanish and French Translations added. Github close #21
* ubuntu: support 'cdvfile' URI
* [CB-6802](https://issues.apache.org/jira/browse/CB-6802) Add license
* Upload progress now works also for second file
* [CB-6706](https://issues.apache.org/jira/browse/CB-6706): Relax dependency on file plugin
* [CB-3440](https://issues.apache.org/jira/browse/CB-3440) [BlackBerry10] Update implementation to use modules from file plugin
* [CB-6378](https://issues.apache.org/jira/browse/CB-6378) Use connection.disconnect() instead of stream.close() for thread-safety
* [CB-6491](https://issues.apache.org/jira/browse/CB-6491) add CONTRIBUTING.md
* [CB-6466](https://issues.apache.org/jira/browse/CB-6466) Auto-create directories in download
* [CB-6494](https://issues.apache.org/jira/browse/CB-6494) android: Fix upload of KitKat content URIs
* Upleveled from android port with following commits: 3c1ff16 Andrew Grieve - [CB-5762](https://issues.apache.org/jira/browse/CB-5762) android: Fix lengthComputable set wrong for gzip downloads 8374b3d Colin Mahoney - [CB-5631](https://issues.apache.org/jira/browse/CB-5631) Removed SimpleTrackingInputStream.read(byte[] buffer) 6f91ac3 Bas Bosman - [CB-4907](https://issues.apache.org/jira/browse/CB-4907) Close stream when we're finished with it 651460f Christoph Neumann - [CB-6000](https://issues.apache.org/jira/browse/CB-6000) Nginx rejects Content-Type without a space before "boundary". 35f80e4 Ian Clelland - [CB-6050](https://issues.apache.org/jira/browse/CB-6050): Use instance method on actual file plugin object to get FileEntry to return on download
* [CB-5980](https://issues.apache.org/jira/browse/CB-5980) Updated version and RELEASENOTES.md for release 0.4.1
### 0.4.3 (Apr 17, 2014)
* [CB-6422](https://issues.apache.org/jira/browse/CB-6422) [windows8] use cordova/exec/proxy
* iOS: Fix error where files were not removed on abort
* [CB-5175](https://issues.apache.org/jira/browse/CB-5175): [ios] CDVFileTransfer asynchronous download (Fixes #24)
* [ios] Cast id references to NSURL to avoid compiler warnings (Fixes: apache/cordova-plugin-file-transfer#18)
* [CB-6212](https://issues.apache.org/jira/browse/CB-6212): [iOS] fix warnings compiled under arm64 64-bit
* [CB-5762](https://issues.apache.org/jira/browse/CB-5762): [FireOS] android: Fix lengthComputable set wrong for gzip downloads
* [CB-5631](https://issues.apache.org/jira/browse/CB-5631): [FireOS] Removed SimpleTrackingInputStream.read(byte[] buffer)
* [CB-4907](https://issues.apache.org/jira/browse/CB-4907): [FireOS] Close stream when we're finished with it
* [CB-6000](https://issues.apache.org/jira/browse/CB-6000): [FireOS] Nginx rejects Content-Type without a space before "boundary".
* [CB-6050](https://issues.apache.org/jira/browse/CB-6050): [FireOS] Use instance method on actual file plugin object to get FileEntry to return on download
* [CB-6460](https://issues.apache.org/jira/browse/CB-6460): Update license headers
### 0.4.2 (Feb 28, 2014)
* [CB-6106](https://issues.apache.org/jira/browse/CB-6106) Ensure that nativeURL is used by file transfer download
* iOS: Fix default value for trustAllHosts on iOS (YES->NO)
* [CB-6059](https://issues.apache.org/jira/browse/CB-6059) iOS: Stop FileTransfer.download doing IO on the UI thread.
* [CB-5588](https://issues.apache.org/jira/browse/CB-5588) iOS: Add response headers to upload result
* [CB-2190](https://issues.apache.org/jira/browse/CB-2190) iOS: Make backgroundTaskId apply to downloads as well. Move backgroundTaskId to the delegate.
* [CB-6050](https://issues.apache.org/jira/browse/CB-6050) Android: Use instance method on actual file plugin object to get FileEntry to return on download
* [CB-6000](https://issues.apache.org/jira/browse/CB-6000) Android: Nginx rejects Content-Type without a space before "boundary".
* [CB-4907](https://issues.apache.org/jira/browse/CB-4907) Android: Close stream when we're finished with it
* [CB-6022](https://issues.apache.org/jira/browse/CB-6022) Add backwards-compatibility notes to doc
### 0.4.1 (Feb 05, 2014)
* [CB-5365](https://issues.apache.org/jira/browse/CB-5365) Remove unused exception var to prevent warnings?
* [CB-2421](https://issues.apache.org/jira/browse/CB-2421) explicitly write the bytesSent,responseCode,result to the FileUploadResult pending release of cordova-plugin-file dependency, added some sanity checks for callbacks
* iOS: Update for new file plugin api
* [CB-5631](https://issues.apache.org/jira/browse/CB-5631) Removed SimpleTrackingInputStream.read(byte[] buffer)
* [CB-5762](https://issues.apache.org/jira/browse/CB-5762) android: Fix lengthComputable set wrong for gzip downloads
* [CB-4899](https://issues.apache.org/jira/browse/CB-4899) [BlackBerry10] Improve binary file transfer download
* Delete stale test/ directory
* [CB-5722](https://issues.apache.org/jira/browse/CB-5722) [BlackBerry10] Update upload function to use native file object
* [CB-5658](https://issues.apache.org/jira/browse/CB-5658) Delete stale snapshot of plugin docs
* Remove @1 designation from file plugin dependency until pushed to npm
* [CB-5466](https://issues.apache.org/jira/browse/CB-5466): Update to work with filesystem URLs
### 0.4.0 (Dec 4, 2013)
* [CB-5466](https://issues.apache.org/jira/browse/CB-5466): Partial revert; we're not ready yet for FS urls
* add ubuntu platform
* [CB-5466](https://issues.apache.org/jira/browse/CB-5466): Minor version bump
* [CB-5466](https://issues.apache.org/jira/browse/CB-5466): Update FileTransfer plugin to accept filesystem urls
* Added amazon-fireos platform. Change to use amazon-fireos as the platform if the user agen string contains 'cordova-amazon-fireos'
### 0.3.4 (Oct 28, 2013)
* [CB-5128](https://issues.apache.org/jira/browse/CB-5128): added repo + issue tag to plugin.xml for file transfer plugin
* [CB-5010](https://issues.apache.org/jira/browse/CB-5010) Incremented plugin version on dev branch.
### 0.3.3 (Oct 9, 2013)
* removed un-needed undef check
* Fix missing headers in Windows 8 upload proxy
* Fix missing headers in Windows 8 Proxy
* Fix Windows 8 HTMLAnchorElement return host:80 which force Basic Auth Header to replace options Auth Header
* [CB-4915](https://issues.apache.org/jira/browse/CB-4915) Incremented plugin version on dev branch.
### 0.3.2 (Sept 25, 2013)
* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) bumping&resetting version
* [windows8] commandProxy was moved
* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) updating core references
* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming org.apache.cordova.core.file-transfer to org.apache.cordova.file-transfer and updating dependency
* Rename CHANGELOG.md -> RELEASENOTES.md

View File

@@ -0,0 +1,311 @@
<!--
# license: Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-->
# cordova-plugin-file-transfer
[![Build Status](https://travis-ci.org/apache/cordova-plugin-file-transfer.svg)](https://travis-ci.org/apache/cordova-plugin-file-transfer)
Plugin-Dokumentation: <doc/index.md>
Dieses Plugin ermöglicht Ihnen zum Hochladen und Herunterladen von Dateien.
Dieses Plugin wird global `FileTransfer`, `FileUploadOptions` Konstruktoren definiert.
Obwohl im globalen Gültigkeitsbereich, sind sie nicht bis nach dem `deviceready`-Ereignis.
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
console.log(FileTransfer);
}
## Installation
cordova plugin add cordova-plugin-file-transfer
## Unterstützte Plattformen
* Amazon Fire OS
* Android
* BlackBerry 10
* Browser
* Firefox OS **
* iOS
* Windows Phone 7 und 8 *
* Windows 8
* Windows
\ * *Unterstützen keine `Onprogress` noch `abort()` *
\ ** * `Onprogress` nicht unterstützt*
# FileTransfer
Das `FileTransfer` -Objekt stellt eine Möglichkeit zum Hochladen von Dateien, die mithilfe einer HTTP-mehrteiligen POST oder PUT-Anforderung, und auch Dateien herunterladen.
## Eigenschaften
* **OnProgress**: aufgerufen, wobei ein `ProgressEvent` wann wird eine neue Datenmenge übertragen. *(Funktion)*
## Methoden
* **Upload**: sendet eine Datei an einen Server.
* **Download**: lädt eine Datei vom Server.
* **abort**: Abbruch eine Übertragung in Bearbeitung.
## Upload
**Parameter**:
* **FileURL**: Dateisystem-URL, das die Datei auf dem Gerät. Für rückwärts Kompatibilität, dies kann auch der vollständige Pfad der Datei auf dem Gerät sein. (Siehe [rückwärts Kompatibilität Notes] unten)
* **Server**: URL des Servers, die Datei zu empfangen, wie kodiert`encodeURI()`.
* **successCallback**: ein Rückruf, der ein `FileUploadResult`-Objekt übergeben wird. *(Funktion)*
* **errorCallback**: ein Rückruf, der ausgeführt wird, tritt ein Fehler beim Abrufen der `FileUploadResult`. Mit einem `FileTransferError`-Objekt aufgerufen. *(Funktion)*
* **Optionen**: optionale Parameter *(Objekt)*. Gültige Schlüssel:
* **FileKey**: der Name des Form-Elements. Wird standardmäßig auf `file` . (DOM-String und enthält)
* **Dateiname**: der Dateiname beim Speichern der Datei auf dem Server verwendet. Wird standardmäßig auf `image.jpg` . (DOM-String und enthält)
* **httpMethod**: die HTTP-Methode, die-entweder `PUT` oder `POST`. Der Standardwert ist `POST`. (DOM-String und enthält)
* **mimeType**: den Mime-Typ der Daten hochzuladen. Standardwerte auf `Image/Jpeg`. (DOM-String und enthält)
* **params**: eine Reihe von optionalen Schlüssel/Wert-Paaren in der HTTP-Anforderung übergeben. (Objekt)
* **chunkedMode**: ob die Daten in "Chunked" streaming-Modus hochladen. Der Standardwert ist `true`. (Boolean)
* **headers**: eine Karte von Header-Name-Header-Werte. Verwenden Sie ein Array, um mehr als einen Wert anzugeben. Auf iOS, FireOS und Android wenn ein Content-Type-Header vorhanden ist, werden mehrteilige Formulardaten nicht verwendet werden. (Object)
* **httpMethod**: die HTTP-Methode zu verwenden, z.B. POST oder PUT. Der Standardwert ist `POST`. (DOM-String enthält)
* **TrustAllHosts**: Optionaler Parameter, wird standardmäßig auf `false` . Wenn legen Sie auf `true` , es akzeptiert alle Sicherheitszertifikate. Dies ist nützlich, da Android selbstsignierte Zertifikate ablehnt. Nicht für den produktiven Einsatz empfohlen. Auf Android und iOS unterstützt. *(Boolean)*
### Beispiel
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function (r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
var fail = function (error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
options.mimeType = "text/plain";
var params = {};
params.value1 = "test";
params.value2 = "param";
options.params = params;
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
### Beispiel mit hochladen Kopf- und Progress-Ereignisse (Android und iOS nur)
function win(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
function fail(error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var uri = encodeURI("http://some.server.com/upload.php");
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1);
options.mimeType="text/plain";
var headers={'headerParam':'headerValue'};
options.headers = headers;
var ft = new FileTransfer();
ft.onprogress = function(progressEvent) {
if (progressEvent.lengthComputable) {
loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
} else {
loadingStatus.increment();
}
};
ft.upload(fileURL, uri, win, fail, options);
## FileUploadResult
Ein `FileUploadResult`-Objekt wird an den Erfolg-Rückruf des `Objekts <code>FileTransfer`-Upload()-Methode</code> übergeben.
### Eigenschaften
* **BytesSent**: die Anzahl der Bytes, die als Teil des Uploads an den Server gesendet. (lange)
* **ResponseCode**: die HTTP-Response-Code vom Server zurückgegeben. (lange)
* **response**: der HTTP-Antwort vom Server zurückgegeben. (DOM-String und enthält)
* **Header**: die HTTP-Response-Header vom Server. (Objekt)
* Derzeit unterstützt auf iOS nur.
### iOS Macken
* Unterstützt keine `responseCode` oder`bytesSent`.
## Download
**Parameter**:
* **source**: URL des Servers, um die Datei herunterzuladen, wie kodiert`encodeURI()`.
* **target**: Dateisystem-Url, das die Datei auf dem Gerät. Für rückwärts Kompatibilität, dies kann auch der vollständige Pfad der Datei auf dem Gerät sein. (Siehe [rückwärts Kompatibilität Notes] unten)
* **SuccessCallback**: ein Rückruf, der übergeben wird ein `FileEntry` Objekt. *(Funktion)*
* **errorCallback**: ein Rückruf, der ausgeführt wird, tritt ein Fehler beim Abrufen der `FileEntry`. Mit einem `FileTransferError`-Objekt aufgerufen. *(Funktion)*
* **TrustAllHosts**: Optionaler Parameter, wird standardmäßig auf `false` . Wenn legen Sie auf `true` , es akzeptiert alle Sicherheitszertifikate. Dies ist nützlich, da Android selbstsignierte Zertifikate ablehnt. Nicht für den produktiven Einsatz empfohlen. Auf Android und iOS unterstützt. *(Boolean)*
* **Options**: optionale Parameter, derzeit nur unterstützt Kopfzeilen (z. B. Autorisierung (Standardauthentifizierung), etc.).
### Beispiel
// !! Assumes variable fileURL contains a valid URL to a path on the device,
// for example, cdvfile://localhost/persistent/path/to/downloads/
var fileTransfer = new FileTransfer();
var uri = encodeURI("http://some.server.com/download.php");
fileTransfer.download(
uri,
fileURL,
function(entry) {
console.log("download complete: " + entry.toURL());
},
function(error) {
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("upload error code" + error.code);
},
false,
{
headers: {
"Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
}
}
);
### WP8 Macken
* Downloaden anfordert, wird von native Implementierung zwischengespeichert wird. Um zu vermeiden, Zwischenspeicherung, übergeben `If-Modified-Since` Header Methode herunterladen.
## abort
Bricht einen in-Progress-Transfer. Der Onerror-Rückruf wird ein FileTransferError-Objekt übergeben, die einen Fehlercode FileTransferError.ABORT_ERR hat.
### Beispiel
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function(r) {
console.log("Should not be called.");
}
var fail = function(error) {
// error.code == FileTransferError.ABORT_ERR
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName="myphoto.jpg";
options.mimeType="image/jpeg";
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
ft.abort();
## FileTransferError
Ein `FileTransferError`-Objekt wird an eine Fehler-Callback übergeben, wenn ein Fehler auftritt.
### Eigenschaften
* **Code**: einer der vordefinierten Fehlercodes aufgeführt. (Anzahl)
* **Quelle**: URL der Quelle. (String)
* **Ziel**: URL zum Ziel. (String)
* **HTTP_STATUS**: HTTP-Statuscode. Dieses Attribut ist nur verfügbar, wenn ein Response-Code aus der HTTP-Verbindung eingeht. (Anzahl)
* **body** Antworttext. Dieses Attribut ist nur verfügbar, wenn eine Antwort von der HTTP-Verbindung eingeht. (String)
* **exception**: entweder e.getMessage oder e.toString (String)
### Konstanten
* 1 = `FileTransferError.FILE_NOT_FOUND_ERR`
* 2 = `FileTransferError.INVALID_URL_ERR`
* 3 = `FileTransferError.CONNECTION_ERR`
* 4 = `FileTransferError.ABORT_ERR`
* 5 = `FileTransferError.NOT_MODIFIED_ERR`
## Hinweise rückwärts Kompatibilität
Frühere Versionen des Plugins würde nur Gerät-Absolute-Dateipfade als Quelle für Uploads oder als Ziel für Downloads übernehmen. Diese Pfade wäre in der Regel der form
/var/mobile/Applications/<application UUID>/Documents/path/to/file (iOS)
/storage/emulated/0/path/to/file (Android)
Für rückwärts Kompatibilität, diese Pfade noch akzeptiert werden, und wenn Ihre Anwendung Pfade wie diese im permanenten Speicher aufgezeichnet hat, dann sie können weiter verwendet werden.
Diese Pfade waren zuvor in der Eigenschaft `fullPath` `FileEntry` und `DirectoryEntry`-Objekte, die durch das Plugin Datei zurückgegeben ausgesetzt. Neue Versionen der die Datei-Erweiterung, jedoch nicht länger werden diese Pfade zu JavaScript.
Wenn Sie ein auf eine neue Upgrade (1.0.0 oder neuere) Version der Datei, und Sie haben zuvor mit `entry.fullPath` als Argumente `download()` oder `upload()`, dann ändern Sie den Code, um die Dateisystem-URLs verwenden müssen.
`FileEntry.toURL()` und `DirectoryEntry.toURL()` zurück, eine Dateisystem-URL in der form
cdvfile://localhost/persistent/path/to/file
die anstelle der absoluten Dateipfad in `download()` und `upload()` Methode verwendet werden kann.

View File

@@ -0,0 +1,302 @@
<!---
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
# cordova-plugin-file-transfer
Dieses Plugin ermöglicht Ihnen zum Hochladen und Herunterladen von Dateien.
Dieses Plugin wird global `FileTransfer`, `FileUploadOptions` Konstruktoren definiert.
Obwohl im globalen Gültigkeitsbereich, sind sie nicht bis nach dem `deviceready`-Ereignis.
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
console.log(FileTransfer);
}
## Installation
cordova plugin add cordova-plugin-file-transfer
## Unterstützte Plattformen
* Amazon Fire OS
* Android
* BlackBerry 10
* Browser
* Firefox OS **
* iOS
* Windows Phone 7 und 8 *
* Windows 8
* Windows
* *Unterstützen keine `onprogress` noch `abort()`*
* * *`onprogress` nicht unterstützt*
# FileTransfer
Das `FileTransfer`-Objekt bietet eine Möglichkeit zum Hochladen von Dateien, die mithilfe einer HTTP-Anforderung für mehrteiligen POST sowie Informationen zum Herunterladen von Dateien sowie.
## Eigenschaften
* **OnProgress**: aufgerufen, wobei ein `ProgressEvent` wann wird eine neue Datenmenge übertragen. *(Funktion)*
## Methoden
* **Upload**: sendet eine Datei an einen Server.
* **Download**: lädt eine Datei vom Server.
* **abort**: Abbruch eine Übertragung in Bearbeitung.
## Upload
**Parameter**:
* **FileURL**: Dateisystem-URL, das die Datei auf dem Gerät. Für rückwärts Kompatibilität, dies kann auch der vollständige Pfad der Datei auf dem Gerät sein. (Siehe [rückwärts Kompatibilität Notes] unten)
* **Server**: URL des Servers, die Datei zu empfangen, wie kodiert`encodeURI()`.
* **successCallback**: ein Rückruf, der ein `FileUploadResult`-Objekt übergeben wird. *(Funktion)*
* **errorCallback**: ein Rückruf, der ausgeführt wird, tritt ein Fehler beim Abrufen der `FileUploadResult`. Mit einem `FileTransferError`-Objekt aufgerufen. *(Funktion)*
* **Optionen**: optionale Parameter *(Objekt)*. Gültige Schlüssel:
* **FileKey**: der Name des Form-Elements. Wird standardmäßig auf `file` . (DOM-String und enthält)
* **Dateiname**: der Dateiname beim Speichern der Datei auf dem Server verwendet. Wird standardmäßig auf `image.jpg` . (DOM-String und enthält)
* **httpMethod**: die HTTP-Methode, die-entweder `PUT` oder `POST`. Der Standardwert ist `POST`. (DOM-String und enthält)
* **mimeType**: den Mime-Typ der Daten hochzuladen. Standardwerte auf `Image/Jpeg`. (DOM-String und enthält)
* **params**: eine Reihe von optionalen Schlüssel/Wert-Paaren in der HTTP-Anforderung übergeben. (Objekt)
* **chunkedMode**: ob die Daten in "Chunked" streaming-Modus hochladen. Der Standardwert ist `true`. (Boolean)
* **headers**: eine Karte von Header-Name-Header-Werte. Verwenden Sie ein Array, um mehr als einen Wert anzugeben. (Objekt)
* **TrustAllHosts**: Optionaler Parameter, wird standardmäßig auf `false` . Wenn legen Sie auf `true` , es akzeptiert alle Sicherheitszertifikate. Dies ist nützlich, da Android selbstsignierte Zertifikate ablehnt. Nicht für den produktiven Einsatz empfohlen. Auf Android und iOS unterstützt. *(Boolean)*
### Beispiel
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function (r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
var fail = function (error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
options.mimeType = "text/plain";
var params = {};
params.value1 = "test";
params.value2 = "param";
options.params = params;
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
### Beispiel mit hochladen Kopf- und Progress-Ereignisse (Android und iOS nur)
function win(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
function fail(error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var uri = encodeURI("http://some.server.com/upload.php");
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1);
options.mimeType="text/plain";
var headers={'headerParam':'headerValue'};
options.headers = headers;
var ft = new FileTransfer();
ft.onprogress = function(progressEvent) {
if (progressEvent.lengthComputable) {
loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
} else {
loadingStatus.increment();
}
};
ft.upload(fileURL, uri, win, fail, options);
## FileUploadResult
Ein `FileUploadResult`-Objekt wird an den Erfolg-Rückruf des `Objekts <code>FileTransfer`-Upload()-Methode</code> übergeben.
### Eigenschaften
* **BytesSent**: die Anzahl der Bytes, die als Teil des Uploads an den Server gesendet. (lange)
* **ResponseCode**: die HTTP-Response-Code vom Server zurückgegeben. (lange)
* **response**: der HTTP-Antwort vom Server zurückgegeben. (DOM-String und enthält)
* **Header**: die HTTP-Response-Header vom Server. (Objekt)
* Derzeit unterstützt auf iOS nur.
### iOS Macken
* Unterstützt keine `responseCode` oder`bytesSent`.
## Download
**Parameter**:
* **source**: URL des Servers, um die Datei herunterzuladen, wie kodiert`encodeURI()`.
* **target**: Dateisystem-Url, das die Datei auf dem Gerät. Für rückwärts Kompatibilität, dies kann auch der vollständige Pfad der Datei auf dem Gerät sein. (Siehe [rückwärts Kompatibilität Notes] unten)
* **SuccessCallback**: ein Rückruf, der übergeben wird ein `FileEntry` Objekt. *(Funktion)*
* **errorCallback**: ein Rückruf, der ausgeführt wird, tritt ein Fehler beim Abrufen der `FileEntry`. Mit einem `FileTransferError`-Objekt aufgerufen. *(Funktion)*
* **TrustAllHosts**: Optionaler Parameter, wird standardmäßig auf `false` . Wenn legen Sie auf `true` , es akzeptiert alle Sicherheitszertifikate. Dies ist nützlich, da Android selbstsignierte Zertifikate ablehnt. Nicht für den produktiven Einsatz empfohlen. Auf Android und iOS unterstützt. *(Boolean)*
* **Options**: optionale Parameter, derzeit nur unterstützt Kopfzeilen (z. B. Autorisierung (Standardauthentifizierung), etc.).
### Beispiel
// !! Assumes variable fileURL contains a valid URL to a path on the device,
// for example, cdvfile://localhost/persistent/path/to/downloads/
var fileTransfer = new FileTransfer();
var uri = encodeURI("http://some.server.com/download.php");
fileTransfer.download(
uri,
fileURL,
function(entry) {
console.log("download complete: " + entry.toURL());
},
function(error) {
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("upload error code" + error.code);
},
false,
{
headers: {
"Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
}
}
);
## abort
Bricht einen in-Progress-Transfer. Der Onerror-Rückruf wird ein FileTransferError-Objekt übergeben, die einen Fehlercode FileTransferError.ABORT_ERR hat.
### Beispiel
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function(r) {
console.log("Should not be called.");
}
var fail = function(error) {
// error.code == FileTransferError.ABORT_ERR
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName="myphoto.jpg";
options.mimeType="image/jpeg";
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
ft.abort();
## FileTransferError
Ein `FileTransferError`-Objekt wird an eine Fehler-Callback übergeben, wenn ein Fehler auftritt.
### Eigenschaften
* **Code**: einer der vordefinierten Fehlercodes aufgeführt. (Anzahl)
* **Quelle**: URL der Quelle. (String)
* **Ziel**: URL zum Ziel. (String)
* **HTTP_STATUS**: HTTP-Statuscode. Dieses Attribut ist nur verfügbar, wenn ein Response-Code aus der HTTP-Verbindung eingeht. (Anzahl)
* **body** Antworttext. Dieses Attribut ist nur verfügbar, wenn eine Antwort von der HTTP-Verbindung eingeht. (String)
* **exception**: entweder e.getMessage oder e.toString (String)
### Konstanten
* 1 = `FileTransferError.FILE_NOT_FOUND_ERR`
* 2 = `FileTransferError.INVALID_URL_ERR`
* 3 = `FileTransferError.CONNECTION_ERR`
* 4 = `FileTransferError.ABORT_ERR`
* 5 = `FileTransferError.NOT_MODIFIED_ERR`
## Hinweise rückwärts Kompatibilität
Frühere Versionen des Plugins würde nur Gerät-Absolute-Dateipfade als Quelle für Uploads oder als Ziel für Downloads übernehmen. Diese Pfade wäre in der Regel der form
/var/mobile/Applications/<application UUID>/Documents/path/to/file (iOS)
/storage/emulated/0/path/to/file (Android)
Für rückwärts Kompatibilität, diese Pfade noch akzeptiert werden, und wenn Ihre Anwendung Pfade wie diese im permanenten Speicher aufgezeichnet hat, dann sie können weiter verwendet werden.
Diese Pfade waren zuvor in der Eigenschaft `fullPath` `FileEntry` und `DirectoryEntry`-Objekte, die durch das Plugin Datei zurückgegeben ausgesetzt. Neue Versionen der die Datei-Erweiterung, jedoch nicht länger werden diese Pfade zu JavaScript.
Wenn Sie ein auf eine neue Upgrade (1.0.0 oder neuere) Version der Datei, und Sie haben zuvor mit `entry.fullPath` als Argumente `download()` oder `upload()`, dann ändern Sie den Code, um die Dateisystem-URLs verwenden müssen.
`FileEntry.toURL()` und `DirectoryEntry.toURL()` zurück, eine Dateisystem-URL in der form
cdvfile://localhost/persistent/path/to/file
die anstelle der absoluten Dateipfad in `download()` und `upload()` Methode verwendet werden kann.

View File

@@ -0,0 +1,311 @@
<!--
# license: Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-->
# cordova-plugin-file-transfer
[![Build Status](https://travis-ci.org/apache/cordova-plugin-file-transfer.svg)](https://travis-ci.org/apache/cordova-plugin-file-transfer)
Documentación del plugin: <doc/index.md>
Este plugin te permite cargar y descargar archivos.
Este plugin define global `FileTransfer` , `FileUploadOptions` constructores.
Aunque en el ámbito global, no están disponibles hasta después de la `deviceready` evento.
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
console.log(FileTransfer);
}
## Instalación
cordova plugin add cordova-plugin-file-transfer
## Plataformas soportadas
* Amazon fire OS
* Android
* BlackBerry 10
* Explorador
* Firefox OS **
* iOS
* Windows Phone 7 y 8 *
* Windows 8
* Windows
\ * *No soporta `onprogress` ni `abort()` *
\ ** *No soporta `onprogress` *
# FileTransfer
El objeto `FileTransfer` proporciona una manera para subir archivos utilizando una varias parte solicitud HTTP POST o PUT y descargar archivos, así.
## Propiedades
* **OnProgress**: llama con un `ProgressEvent` cuando se transfiere un nuevo paquete de datos. *(Función)*
## Métodos
* **cargar**: envía un archivo a un servidor.
* **Descargar**: descarga un archivo del servidor.
* **abortar**: aborta una transferencia en curso.
## subir
**Parámetros**:
* **fileURL**: URL de Filesystem que representa el archivo en el dispositivo. Para atrás compatibilidad, esto también puede ser la ruta de acceso completa del archivo en el dispositivo. (Ver [hacia atrás compatibilidad notas] debajo)
* **servidor**: dirección URL del servidor para recibir el archivo, como codificada por`encodeURI()`.
* **successCallback**: una devolución de llamada que se pasa un `FileUploadResult` objeto. *(Función)*
* **errorCallback**: una devolución de llamada que se ejecuta si se produce un error recuperar la `FileUploadResult` . Invocado con un `FileTransferError` objeto. *(Función)*
* **Opciones**: parámetros opcionales *(objeto)*. Teclas válidas:
* **fileKey**: el nombre del elemento de formulario. Por defecto es `file` . (DOMString)
* **nombre de archivo**: el nombre del archivo a utilizar al guardar el archivo en el servidor. Por defecto es `image.jpg` . (DOMString)
* **httpMethod**: método HTTP el utilizar - o `PUT` o `POST` . Por defecto es `POST` . (DOMString)
* **mimeType**: el tipo mime de los datos para cargar. Por defecto es `image/jpeg` . (DOMString)
* **params**: un conjunto de pares clave/valor opcional para pasar en la petición HTTP. (Objeto)
* **chunkedMode**: Si desea cargar los datos en modo de transmisión fragmentado. Por defecto es `true` . (Boolean)
* **headers**: un mapa de nombre de encabezado/valores de encabezado Utilice una matriz para especificar más de un valor. En iOS FireOS y Android, si existe un encabezado llamado Content-Type, datos de un formulario multipart no se utilizará. (Object)
* **httpMethod**: HTTP el método a utilizar por ejemplo POST o poner. Por defecto `el POST`. (DOMString)
* **trustAllHosts**: parámetro opcional, por defecto es `false` . Si establece en `true` , acepta todos los certificados de seguridad. Esto es útil ya que Android rechaza certificados autofirmados seguridad. No se recomienda para uso productivo. Compatible con iOS y Android. *(boolean)*
### Ejemplo
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function (r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
var fail = function (error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
options.mimeType = "text/plain";
var params = {};
params.value1 = "test";
params.value2 = "param";
options.params = params;
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
### Ejemplo con cabeceras de subir y eventos de progreso (Android y iOS solamente)
function win(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
function fail(error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var uri = encodeURI("http://some.server.com/upload.php");
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1);
options.mimeType="text/plain";
var headers={'headerParam':'headerValue'};
options.headers = headers;
var ft = new FileTransfer();
ft.onprogress = function(progressEvent) {
if (progressEvent.lengthComputable) {
loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
} else {
loadingStatus.increment();
}
};
ft.upload(fileURL, uri, win, fail, options);
## FileUploadResult
A `FileUploadResult` objeto se pasa a la devolución del éxito de la `FileTransfer` del objeto `upload()` método.
### Propiedades
* **bytesSent**: el número de bytes enviados al servidor como parte de la carga. (largo)
* **responseCode**: código de respuesta HTTP el devuelto por el servidor. (largo)
* **respuesta**: respuesta el HTTP devuelto por el servidor. (DOMString)
* **cabeceras**: cabeceras de respuesta HTTP el por el servidor. (Objeto)
* Actualmente compatible con iOS solamente.
### iOS rarezas
* No es compatible con `responseCode` o`bytesSent`.
## descargar
**Parámetros**:
* **fuente**: dirección URL del servidor para descargar el archivo, como codificada por`encodeURI()`.
* **objetivo**: Filesystem url que representa el archivo en el dispositivo. Para atrás compatibilidad, esto también puede ser la ruta de acceso completa del archivo en el dispositivo. (Ver [hacia atrás compatibilidad notas] debajo)
* **successCallback**: una devolución de llamada que se pasa un `FileEntry` objeto. *(Función)*
* **errorCallback**: una devolución de llamada que se ejecuta si se produce un error al recuperar los `FileEntry` . Invocado con un `FileTransferError` objeto. *(Función)*
* **trustAllHosts**: parámetro opcional, por defecto es `false` . Si establece en `true` , acepta todos los certificados de seguridad. Esto es útil porque Android rechaza certificados autofirmados seguridad. No se recomienda para uso productivo. Compatible con iOS y Android. *(boolean)*
* **Opciones**: parámetros opcionales, actualmente sólo soporta cabeceras (como autorización (autenticación básica), etc.).
### Ejemplo
// !! Assumes variable fileURL contains a valid URL to a path on the device,
// for example, cdvfile://localhost/persistent/path/to/downloads/
var fileTransfer = new FileTransfer();
var uri = encodeURI("http://some.server.com/download.php");
fileTransfer.download(
uri,
fileURL,
function(entry) {
console.log("download complete: " + entry.toURL());
},
function(error) {
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("upload error code" + error.code);
},
false,
{
headers: {
"Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
}
}
);
### Rarezas de WP8
* Descargar pide se almacena en caché por aplicación nativa. Para evitar el almacenamiento en caché, pasar `if-Modified-Since` encabezado para descargar el método.
## abortar
Aborta a una transferencia en curso. El callback onerror se pasa un objeto FileTransferError que tiene un código de error de FileTransferError.ABORT_ERR.
### Ejemplo
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function(r) {
console.log("Should not be called.");
}
var fail = function(error) {
// error.code == FileTransferError.ABORT_ERR
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName="myphoto.jpg";
options.mimeType="image/jpeg";
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
ft.abort();
## FileTransferError
A `FileTransferError` objeto se pasa a un callback de error cuando se produce un error.
### Propiedades
* **código**: uno de los códigos de error predefinido enumerados a continuación. (Número)
* **fuente**: URL a la fuente. (String)
* **objetivo**: URL a la meta. (String)
* **HTTP_STATUS**: código de estado HTTP. Este atributo sólo está disponible cuando se recibe un código de respuesta de la conexión HTTP. (Número)
* **cuerpo** Cuerpo de la respuesta. Este atributo sólo está disponible cuando se recibe una respuesta de la conexión HTTP. (String)
* **excepción**: cualquier e.getMessage o e.toString (String)
### Constantes
* 1 = `FileTransferError.FILE_NOT_FOUND_ERR`
* 2 = `FileTransferError.INVALID_URL_ERR`
* 3 = `FileTransferError.CONNECTION_ERR`
* 4 = `FileTransferError.ABORT_ERR`
* 5 = `FileTransferError.NOT_MODIFIED_ERR`
## Al revés notas de compatibilidad
Versiones anteriores de este plugin sólo aceptaría dispositivo-absoluto-archivo-rutas como la fuente de carga, o como destino para las descargas. Estos caminos normalmente sería de la forma
/var/mobile/Applications/<application UUID>/Documents/path/to/file (iOS)
/storage/emulated/0/path/to/file (Android)
Para atrás compatibilidad, estos caminos son aceptados todavía, y si su solicitud ha grabado caminos como éstos en almacenamiento persistente, entonces pueden seguir utilizarse.
Estos caminos fueron expuestos anteriormente en el `fullPath` propiedad de `FileEntry` y `DirectoryEntry` objetos devueltos por el plugin de archivo. Las nuevas versiones del archivo plugin, sin embargo, ya no exponen estos caminos a JavaScript.
Si va a actualizar a una nueva (1.0.0 o más reciente) versión del archivo y previamente han estado utilizando `entry.fullPath` como argumentos para `download()` o `upload()` , entonces tendrá que cambiar su código para usar URLs de sistema de archivos en su lugar.
`FileEntry.toURL()`y `DirectoryEntry.toURL()` devolver un filesystem dirección URL de la forma
cdvfile://localhost/persistent/path/to/file
que puede ser utilizado en lugar de la ruta del archivo absoluta tanto en `download()` y `upload()` los métodos.

View File

@@ -0,0 +1,262 @@
<!---
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
# cordova-plugin-file-transfer
Este plugin te permite cargar y descargar archivos.
Este plugin define global `FileTransfer` , `FileUploadOptions` constructores.
Aunque en el ámbito global, no están disponibles hasta después de la `deviceready` evento.
document.addEventListener ("deviceready", onDeviceReady, false);
function onDeviceReady() {console.log(FileTransfer)};
## Instalación
Cordova plugin añade cordova-plugin-file-transferencia
## Plataformas soportadas
* Amazon fire OS
* Android
* BlackBerry 10
* Explorador
* Firefox OS **
* iOS
* Windows Phone 7 y 8 *
* Windows 8
* Windows
* *No son compatibles con `onprogress` ni `abort()` *
** *No son compatibles con `onprogress` *
# FileTransfer
El `FileTransfer` objeto proporciona una manera de subir archivos mediante una solicitud HTTP de POST varias parte y para descargar archivos.
## Propiedades
* **OnProgress**: llama con un `ProgressEvent` cuando se transfiere un nuevo paquete de datos. *(Función)*
## Métodos
* **cargar**: envía un archivo a un servidor.
* **Descargar**: descarga un archivo del servidor.
* **abortar**: aborta una transferencia en curso.
## subir
**Parámetros**:
* **fileURL**: URL de Filesystem que representa el archivo en el dispositivo. Para atrás compatibilidad, esto también puede ser la ruta de acceso completa del archivo en el dispositivo. (Ver [hacia atrás compatibilidad notas] debajo)
* **servidor**: dirección URL del servidor para recibir el archivo, como codificada por`encodeURI()`.
* **successCallback**: una devolución de llamada que se pasa un `FileUploadResult` objeto. *(Función)*
* **errorCallback**: una devolución de llamada que se ejecuta si se produce un error recuperar la `FileUploadResult` . Invocado con un `FileTransferError` objeto. *(Función)*
* **Opciones**: parámetros opcionales *(objeto)*. Teclas válidas:
* **fileKey**: el nombre del elemento de formulario. Por defecto es `file` . (DOMString)
* **nombre de archivo**: el nombre del archivo a utilizar al guardar el archivo en el servidor. Por defecto es `image.jpg` . (DOMString)
* **httpMethod**: método HTTP el utilizar - o `PUT` o `POST` . Por defecto es `POST` . (DOMString)
* **mimeType**: el tipo mime de los datos para cargar. Por defecto es `image/jpeg` . (DOMString)
* **params**: un conjunto de pares clave/valor opcional para pasar en la petición HTTP. (Objeto)
* **chunkedMode**: Si desea cargar los datos en modo de transmisión fragmentado. Por defecto es `true` . (Boolean)
* **cabeceras**: un mapa de valores de encabezado nombre/cabecera. Utilice una matriz para especificar más de un valor. (Objeto)
* **trustAllHosts**: parámetro opcional, por defecto es `false` . Si establece en `true` , acepta todos los certificados de seguridad. Esto es útil ya que Android rechaza certificados autofirmados seguridad. No se recomienda para uso productivo. Compatible con iOS y Android. *(boolean)*
### Ejemplo
// !! Asume fileURL variable contiene una dirección URL válida a un archivo de texto en el dispositivo, / / por ejemplo, ganar var cdvfile://localhost/persistent/path/to/file.txt = function (r) {console.log ("código =" + r.responseCode);
Console.log ("respuesta =" + r.response);
Console.log ("Sent =" + r.bytesSent);}
var fallar = function (error) {alert ("ha ocurrido un error: código =" + error.code);
Console.log ("error al cargar el origen" + error.source);
Console.log ("upload error objetivo" + error.target);}
var opciones = new FileUploadOptions();
options.fileKey = "file";
options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
options.mimeType = "text/plain";
var params = {};
params.value1 = "prueba";
params.value2 = "param";
options.params = params;
var ft = new FileTransfer();
Ft.upload (fileURL, encodeURI ("http://some.server.com/upload.php"), win, fail, opciones);
### Ejemplo con cabeceras de subir y eventos de progreso (Android y iOS solamente)
function win(r) {console.log ("código =" + r.responseCode);
Console.log ("respuesta =" + r.response);
Console.log ("Sent =" + r.bytesSent);}
function fail(error) {alert ("ha ocurrido un error: código =" + error.code);
Console.log ("error al cargar el origen" + error.source);
Console.log ("upload error objetivo" + error.target);}
var uri = encodeURI ("http://some.server.com/upload.php");
var opciones = new FileUploadOptions();
options.fileKey="file";
options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1);
options.mimeType="text/plain";
cabeceras de var ={'headerParam':'headerValue'};
options.headers = encabezados;
var ft = new FileTransfer();
Ft.OnProgress = function(progressEvent) {si (progressEvent.lengthComputable) {loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
} {loadingStatus.increment() más;
}
};
Ft.upload (fileURL, uri, win, fail, opciones);
## FileUploadResult
A `FileUploadResult` objeto se pasa a la devolución del éxito de la `FileTransfer` del objeto `upload()` método.
### Propiedades
* **bytesSent**: el número de bytes enviados al servidor como parte de la carga. (largo)
* **responseCode**: código de respuesta HTTP el devuelto por el servidor. (largo)
* **respuesta**: respuesta el HTTP devuelto por el servidor. (DOMString)
* **cabeceras**: cabeceras de respuesta HTTP el por el servidor. (Objeto)
* Actualmente compatible con iOS solamente.
### iOS rarezas
* No es compatible con `responseCode` o`bytesSent`.
## descargar
**Parámetros**:
* **fuente**: dirección URL del servidor para descargar el archivo, como codificada por`encodeURI()`.
* **objetivo**: Filesystem url que representa el archivo en el dispositivo. Para atrás compatibilidad, esto también puede ser la ruta de acceso completa del archivo en el dispositivo. (Ver [hacia atrás compatibilidad notas] debajo)
* **successCallback**: una devolución de llamada que se pasa un `FileEntry` objeto. *(Función)*
* **errorCallback**: una devolución de llamada que se ejecuta si se produce un error al recuperar los `FileEntry` . Invocado con un `FileTransferError` objeto. *(Función)*
* **trustAllHosts**: parámetro opcional, por defecto es `false` . Si establece en `true` , acepta todos los certificados de seguridad. Esto es útil porque Android rechaza certificados autofirmados seguridad. No se recomienda para uso productivo. Compatible con iOS y Android. *(boolean)*
* **Opciones**: parámetros opcionales, actualmente sólo soporta cabeceras (como autorización (autenticación básica), etc.).
### Ejemplo
// !! Asume fileURL variable contiene una dirección URL válida a un camino en el dispositivo, / / por ejemplo, File Transfer var cdvfile://localhost/persistent/path/to/downloads/ = new FileTransfer();
var uri = encodeURI ("http://some.server.com/download.php");
fileTransfer.download (uri, fileURL, function(entry) {console.log ("descarga completa:" + entry.toURL());
}, function(error) {console.log ("error al descargar el origen" + error.source);
Console.log ("descargar error objetivo" + error.target);
Console.log ("código de error de carga" + error.code);
}, falso, {encabezados: {"Autorización": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA =="}});
## abortar
Aborta a una transferencia en curso. El callback onerror se pasa un objeto FileTransferError que tiene un código de error de FileTransferError.ABORT_ERR.
### Ejemplo
// !! Asume fileURL variable contiene una dirección URL válida a un archivo de texto en el dispositivo, / / por ejemplo, ganar cdvfile://localhost/persistent/path/to/file.txt var function(r) = {console.log ("no se debe llamar.");}
var fallar = function(error) {/ / error.code == FileTransferError.ABORT_ERR alert ("ha ocurrido un error: código =" + error.code);
Console.log ("error al cargar el origen" + error.source);
Console.log ("upload error objetivo" + error.target);}
var opciones = new FileUploadOptions();
options.fileKey="file";
options.fileName="myphoto.jpg";
options.mimeType="image/jpeg";
var ft = new FileTransfer();
Ft.upload (fileURL, encodeURI ("http://some.server.com/upload.php"), win, fail, opciones);
Ft.Abort();
## FileTransferError
A `FileTransferError` objeto se pasa a un callback de error cuando se produce un error.
### Propiedades
* **código**: uno de los códigos de error predefinido enumerados a continuación. (Número)
* **fuente**: URL a la fuente. (String)
* **objetivo**: URL a la meta. (String)
* **HTTP_STATUS**: código de estado HTTP. Este atributo sólo está disponible cuando se recibe un código de respuesta de la conexión HTTP. (Número)
* **cuerpo** Cuerpo de la respuesta. Este atributo sólo está disponible cuando se recibe una respuesta de la conexión HTTP. (String)
* **excepción**: cualquier e.getMessage o e.toString (String)
### Constantes
* 1 = `FileTransferError.FILE_NOT_FOUND_ERR`
* 2 = `FileTransferError.INVALID_URL_ERR`
* 3 = `FileTransferError.CONNECTION_ERR`
* 4 = `FileTransferError.ABORT_ERR`
* 5 = `FileTransferError.NOT_MODIFIED_ERR`
## Al revés notas de compatibilidad
Versiones anteriores de este plugin sólo aceptaría dispositivo-absoluto-archivo-rutas como la fuente de carga, o como destino para las descargas. Estos caminos normalmente sería de la forma
/var/mobile/Applications/<application UUID>/Documents/path/to/file (iOS)
/storage/emulated/0/path/to/file (Android)
Para atrás compatibilidad, estos caminos son aceptados todavía, y si su solicitud ha grabado caminos como éstos en almacenamiento persistente, entonces pueden seguir utilizarse.
Estos caminos fueron expuestos anteriormente en el `fullPath` propiedad de `FileEntry` y `DirectoryEntry` objetos devueltos por el plugin de archivo. Las nuevas versiones del archivo plugin, sin embargo, ya no exponen estos caminos a JavaScript.
Si va a actualizar a una nueva (1.0.0 o más reciente) versión del archivo y previamente han estado utilizando `entry.fullPath` como argumentos para `download()` o `upload()` , entonces tendrá que cambiar su código para usar URLs de sistema de archivos en su lugar.
`FileEntry.toURL()`y `DirectoryEntry.toURL()` devolver un filesystem dirección URL de la forma
cdvfile://localhost/persistent/path/to/file
que puede ser utilizado en lugar de la ruta del archivo absoluta tanto en `download()` y `upload()` los métodos.

View File

@@ -0,0 +1,270 @@
<!--
# license: Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-->
# cordova-plugin-file-transfer
[![Build Status](https://travis-ci.org/apache/cordova-plugin-file-transfer.svg)](https://travis-ci.org/apache/cordova-plugin-file-transfer)
Documentation du plugin : <doc/index.md>
Ce plugin vous permet de télécharger des fichiers.
Ce plugin définit global `FileTransfer` , `FileUploadOptions` constructeurs.
Bien que dans la portée globale, ils ne sont pas disponibles jusqu'après la `deviceready` événement.
document.addEventListener (« deviceready », onDeviceReady, false) ;
function onDeviceReady() {console.log(FileTransfer);}
## Installation
cordova plugin add cordova-plugin-file-transfer
## Plates-formes supportées
* Amazon Fire OS
* Android
* BlackBerry 10
* Navigateur
* Firefox OS **
* iOS
* Windows Phone 7 et 8 *
* Windows 8
* Windows
\ * *Ne supportent pas `onprogress` ni `abort()` *
\ ** *Ne prennent pas en charge les `onprogress` *
# Transfert de fichiers
L'objet de `FileTransfer` fournit un moyen de télécharger des fichiers à l'aide d'une requête HTTP multi-part POST ou PUT et pour télécharger des fichiers.
## Propriétés
* **onprogress** : fonction appelée avec un `ProgressEvent` à chaque fois qu'un nouveau segment de données est transféré. *(Function)*
## Méthodes
* **upload** : envoie un fichier à un serveur.
* **download** : télécharge un fichier depuis un serveur.
* **abort** : annule le transfert en cours.
## upload
**Paramètres**:
* **fileURL** : système de fichiers URL représentant le fichier sur le périphérique. Pour la compatibilité ascendante, cela peut aussi être le chemin complet du fichier sur le périphérique. (Voir [Backwards Compatibility Notes] ci-dessous)
* **server** : l'URL du serveur destiné à recevoir le fichier, encodée via `encodeURI()`.
* **successCallback**: un rappel passé un `FileUploadResult` objet. *(Fonction)*
* **errorCallback**: un rappel qui s'exécute si une erreur survient récupérer la `FileUploadResult` . Appelée avec un `FileTransferError` objet. *(Fonction)*
* **options**: paramètres facultatifs *(objet)*. Clés valides :
* **fileKey**: le nom de l'élément form. Valeur par défaut est `file` . (DOMString)
* **fileName**: le nom de fichier à utiliser lorsque vous enregistrez le fichier sur le serveur. Valeur par défaut est `image.jpg` . (DOMString)
* **httpMethod**: méthode de The HTTP à utiliser - soit `PUT` ou `POST` . Valeur par défaut est `POST` . (DOMString)
* **type MIME**: le type mime des données à télécharger. Valeur par défaut est `image/jpeg` . (DOMString)
* **params**: un ensemble de paires clé/valeur facultative pour passer dans la requête HTTP. (Objet)
* **chunkedMode**: s'il faut télécharger les données en mode streaming mémorisé en bloc. Valeur par défaut est `true` . (Boolean)
* **headers**: une carte des valeurs d'en-tête en-tête/nom. Un tableau permet de spécifier plusieurs valeurs. Sur iOS, FireOS et Android, si un en-tête nommé Content-Type n'est présent, les données de formulaire multipart servira pas. (Object)
* **httpMethod**: The HTTP méthode à utiliser par exemple poster ou mis. Par défaut, `message`. (DOMString)
* **trustAllHosts**: paramètre facultatif, valeur par défaut est `false` . Si la valeur est `true` , il accepte tous les certificats de sécurité. Ceci est utile car Android rejette des certificats auto-signés. N'est pas recommandé pour une utilisation en production. Supporté sur Android et iOS. *(booléen)*
### Exemple
// !! Suppose fileURL variable contient une URL valide dans un fichier texte sur le périphérique, / / par exemple, cdvfile://localhost/persistent/path/to/file.txt var win = function (r) {console.log ("Code =" + r.responseCode) ;
Console.log ("réponse =" + r.response) ;
Console.log ("envoyés =" + r.bytesSent);}
échouer var = function (erreur) {alert ("une erreur est survenue : Code =" + error.code) ;
Console.log (« source de l'erreur de téléchargement » + error.source) ;
Console.log ("erreur de téléchargement cible" + error.target);}
options de var = new FileUploadOptions() ;
options.fileKey = « fichier » ;
options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1) ;
options.mimeType = « text/plain » ;
var params = {} ;
params.value1 = « test » ;
params.Value2 = « param » ;
options.params = params ;
ft var = new FileTransfer() ;
ft.upload (fileURL, encodeURI ("http://some.server.com/upload.php"), win, fail, options) ;
### Exemple avec téléchargement du Header et des Progress Events (Android et iOS uniquement)
function win(r) {console.log ("Code =" + r.responseCode) ;
Console.log ("réponse =" + r.response) ;
Console.log ("envoyés =" + r.bytesSent);}
function fail(error) {alert ("une erreur est survenue : Code =" + error.code) ;
Console.log (« source de l'erreur de téléchargement » + error.source) ;
Console.log ("erreur de téléchargement cible" + error.target);}
var uri = encodeURI ("http://some.server.com/upload.php") ;
options de var = new FileUploadOptions() ;
options.fileKey="file" ;
options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1) ;
options.mimeType="text/plain" ;
en-têtes var ={'headerParam':'headerValue'} ;
options.Headers = en-têtes ;
ft var = new FileTransfer() ;
ft.OnProgress = function(progressEvent) {si (progressEvent.lengthComputable) {loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total) ;
} else {loadingStatus.increment() ;
}
};
ft.upload (fileURL, uri, win, fail, options) ;
## FileUploadResult
A `FileUploadResult` objet est passé au rappel de succès la `FileTransfer` de l'objet `upload()` méthode.
### Propriétés
* **bytesSent** : le nombre d'octets envoyés au serveur dans le cadre du téléchargement. (long)
* **responseCode** : le code de réponse HTTP retourné par le serveur. (long)
* **response** : la réponse HTTP renvoyée par le serveur. (DOMString)
* **en-têtes** : en-têtes de réponse HTTP par le serveur. (Objet)
* Actuellement pris en charge sur iOS seulement.
### Notes au sujet d'iOS
* Ne prend pas en charge les propriétés `responseCode` et `bytesSent`.
## download
**Paramètres**:
* **source** : l'URL du serveur depuis lequel télécharger le fichier, encodée via `encodeURI()`.
* **target** : système de fichiers url représentant le fichier sur le périphérique. Pour la compatibilité ascendante, cela peut aussi être le chemin complet du fichier sur le périphérique. (Voir [Backwards Compatibility Notes] ci-dessous)
* **successCallback** : une callback de succès à laquelle est passée un objet `FileEntry`. *(Function)*
* **errorCallback**: un rappel qui s'exécute si une erreur se produit lors de la récupération du `FileEntry` . Appelée avec un `FileTransferError` objet. *(Fonction)*
* **trustAllHosts**: paramètre facultatif, valeur par défaut est `false` . Si la valeur est `true` , il accepte tous les certificats de sécurité. Ceci peut être utile car Android rejette les certificats auto-signés. N'est pas recommandé pour une utilisation en production. Supporté sur Android et iOS. *(booléen)*
* **options** : paramètres facultatifs, seules les en-têtes sont actuellement supportées (par exemple l'autorisation (authentification basique), etc.).
### Exemple
// !! Suppose fileURL variable contient une URL valide vers un chemin d'accès sur le périphérique, / / par exemple, transfert de fichiers var cdvfile://localhost/persistent/path/to/downloads/ = new FileTransfer() ;
var uri = encodeURI ("http://some.server.com/download.php") ;
fileTransfer.download (uri, fileURL, function(entry) {console.log ("téléchargement complet:" + entry.toURL()) ;
}, function(error) {console.log (« source de l'erreur de téléchargement » + error.source) ;
Console.log (« erreur de téléchargement cible » + error.target) ;
Console.log (« code d'erreur de téléchargement » + error.code) ;
}, faux, {en-têtes: {« Autorisation »: « dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA base == "}}) ;
### Quirks wp8
* Télécharger demande est mis en cache par l'implémentation native. Pour éviter la mise en cache, pass `if-Modified-Since` en-tête Télécharger méthode.
## abort
Abandonne un transfert en cours. Le rappel onerror est passé à un objet FileTransferError qui a un code d'erreur de FileTransferError.ABORT_ERR.
### Exemple
// !! Suppose fileURL variable contient une URL valide dans un fichier texte sur le périphérique, / / par exemple, cdvfile://localhost/persistent/path/to/file.txt var win = function(r) {console.log ("ne devrait pas être appelée.");}
var fail = function(error) {/ / error.code == FileTransferError.ABORT_ERR alert ("une erreur est survenue : Code =" + error.code) ;
Console.log (« source de l'erreur de téléchargement » + error.source) ;
Console.log ("erreur de téléchargement cible" + error.target);}
options de var = new FileUploadOptions() ;
options.fileKey="file" ;
options.fileName="myphoto.jpg" ;
options.mimeType="image/jpeg" ;
ft var = new FileTransfer() ;
ft.upload (fileURL, encodeURI ("http://some.server.com/upload.php"), win, fail, options) ;
ft.Abort() ;
## FileTransferError
A `FileTransferError` objet est passé à un rappel d'erreur lorsqu'une erreur survient.
### Propriétés
* **code** : l'un des codes d'erreur prédéfinis énumérés ci-dessous. (Number)
* **source** : l'URI de la source. (String)
* **target**: l'URI de la destination. (String)
* **http_status** : code d'état HTTP. Cet attribut n'est disponible que lorsqu'un code de réponse est fourni via la connexion HTTP. (Number)
* **corps** Corps de réponse. Cet attribut n'est disponible que lorsqu'une réponse est reçue de la connexion HTTP. (String)
* **exception**: soit e.getMessage ou e.toString (String)
### Constantes
* 1 = `FileTransferError.FILE_NOT_FOUND_ERR`
* 2 = `FileTransferError.INVALID_URL_ERR`
* 3 = `FileTransferError.CONNECTION_ERR`
* 4 = `FileTransferError.ABORT_ERR`
* 5 = `FileTransferError.NOT_MODIFIED_ERR`
## Backwards Compatibility Notes
Les versions précédentes de ce plugin n'accepterait périphérique--fichier-chemins d'accès absolus comme source pour les téléchargements, ou comme cible pour les téléchargements. Ces chemins seraient généralement de la forme
/ var/mobile/Applications/< application UUID >/Documents/chemin/vers/fichier (iOS), /storage/emulated/0/path/to/file (Android)
Pour vers l'arrière la compatibilité, ces chemins sont toujours acceptés, et si votre application a enregistré des chemins comme celles-ci dans un stockage persistant, alors ils peuvent continuer à être utilisé.
Ces chemins ont été précédemment exposés dans le `fullPath` propriété de `FileEntry` et `DirectoryEntry` les objets retournés par le fichier plugin. Nouvelles versions du fichier plugin, cependant, ne plus exposent ces chemins à JavaScript.
Si vous migrez vers une nouvelle (1.0.0 ou plus récent) version de fichier et vous avez précédemment utilisé `entry.fullPath` comme arguments à `download()` ou `upload()` , alors vous aurez besoin de modifier votre code pour utiliser le système de fichiers URL au lieu de cela.
`FileEntry.toURL()`et `DirectoryEntry.toURL()` retournent une URL de système de fichiers du formulaire
cdvfile://localhost/persistent/path/to/file
qui peut être utilisé à la place le chemin d'accès absolu au fichier dans les deux `download()` et `upload()` méthodes.

View File

@@ -0,0 +1,261 @@
<!---
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
# cordova-plugin-file-transfer
Ce plugin vous permet de télécharger des fichiers.
Ce plugin définit global `FileTransfer` , `FileUploadOptions` constructeurs.
Bien que dans la portée globale, ils ne sont pas disponibles jusqu'après la `deviceready` événement.
document.addEventListener (« deviceready », onDeviceReady, false) ;
function onDeviceReady() {console.log(FileTransfer);}
## Installation
Cordova plugin ajouter cordova-plugin-file-transfert
## Plates-formes prises en charge
* Amazon Fire OS
* Android
* BlackBerry 10
* Navigateur
* Firefox OS **
* iOS
* Windows Phone 7 et 8 *
* Windows 8
* Windows
* *Ne supportent pas `onprogress` ni `abort()` *
** *Ne prennent pas en charge `onprogress` *
# Transfert de fichiers
Le `FileTransfer` objet fournit un moyen de télécharger des fichiers à l'aide d'une requête HTTP de la poste plusieurs partie et pour télécharger des fichiers aussi bien.
## Propriétés
* **onprogress** : fonction appelée avec un `ProgressEvent` à chaque fois qu'un nouveau segment de données est transféré. *(Function)*
## Méthodes
* **upload** : envoie un fichier à un serveur.
* **download** : télécharge un fichier depuis un serveur.
* **abort** : annule le transfert en cours.
## upload
**Paramètres**:
* **fileURL** : système de fichiers URL représentant le fichier sur le périphérique. Pour la compatibilité ascendante, cela peut aussi être le chemin complet du fichier sur le périphérique. (Voir [Backwards Compatibility Notes] ci-dessous)
* **server** : l'URL du serveur destiné à recevoir le fichier, encodée via `encodeURI()`.
* **successCallback**: un rappel passé un `FileUploadResult` objet. *(Fonction)*
* **errorCallback**: un rappel qui s'exécute si une erreur survient récupérer la `FileUploadResult` . Appelée avec un `FileTransferError` objet. *(Fonction)*
* **options**: paramètres facultatifs *(objet)*. Clés valides :
* **fileKey**: le nom de l'élément form. Valeur par défaut est `file` . (DOMString)
* **fileName**: le nom de fichier à utiliser lorsque vous enregistrez le fichier sur le serveur. Valeur par défaut est `image.jpg` . (DOMString)
* **httpMethod**: méthode de The HTTP à utiliser - soit `PUT` ou `POST` . Valeur par défaut est `POST` . (DOMString)
* **type MIME**: le type mime des données à télécharger. Valeur par défaut est `image/jpeg` . (DOMString)
* **params**: un ensemble de paires clé/valeur facultative pour passer dans la requête HTTP. (Objet)
* **chunkedMode**: s'il faut télécharger les données en mode streaming mémorisé en bloc. Valeur par défaut est `true` . (Boolean)
* **en-têtes**: une carte des valeurs d'en-tête en-tête/nom. Un tableau permet de spécifier plusieurs valeurs. (Objet)
* **trustAllHosts**: paramètre facultatif, valeur par défaut est `false` . Si la valeur `true` , il accepte tous les certificats de sécurité. Ceci est utile car Android rejette des certificats auto-signés. Non recommandé pour une utilisation de production. Supporté sur Android et iOS. *(boolean)*
### Exemple
// !! Suppose fileURL variable contient une URL valide dans un fichier texte sur le périphérique, / / par exemple, cdvfile://localhost/persistent/path/to/file.txt var win = function (r) {console.log ("Code =" + r.responseCode) ;
Console.log ("réponse =" + r.response) ;
Console.log ("envoyés =" + r.bytesSent);}
échouer var = function (erreur) {alert ("une erreur est survenue : Code =" + error.code) ;
Console.log (« source de l'erreur de téléchargement » + error.source) ;
Console.log ("erreur de téléchargement cible" + error.target);}
options de var = new FileUploadOptions() ;
options.fileKey = « fichier » ;
options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1) ;
options.mimeType = « text/plain » ;
var params = {} ;
params.value1 = « test » ;
params.Value2 = « param » ;
options.params = params ;
ft var = new FileTransfer() ;
ft.upload (fileURL, encodeURI ("http://some.server.com/upload.php"), win, fail, options) ;
### Exemple avec téléchargement du Header et des Progress Events (Android et iOS uniquement)
function win(r) {console.log ("Code =" + r.responseCode) ;
Console.log ("réponse =" + r.response) ;
Console.log ("envoyés =" + r.bytesSent);}
function fail(error) {alert ("une erreur est survenue : Code =" + error.code) ;
Console.log (« source de l'erreur de téléchargement » + error.source) ;
Console.log ("erreur de téléchargement cible" + error.target);}
var uri = encodeURI ("http://some.server.com/upload.php") ;
options de var = new FileUploadOptions() ;
options.fileKey="file" ;
options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1) ;
options.mimeType="text/plain" ;
en-têtes var ={'headerParam':'headerValue'} ;
options.Headers = en-têtes ;
ft var = new FileTransfer() ;
ft.OnProgress = function(progressEvent) {si (progressEvent.lengthComputable) {loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total) ;
} else {loadingStatus.increment() ;
}
};
ft.upload (fileURL, uri, win, fail, options) ;
## FileUploadResult
A `FileUploadResult` objet est passé au rappel de succès la `FileTransfer` de l'objet `upload()` méthode.
### Propriétés
* **bytesSent** : le nombre d'octets envoyés au serveur dans le cadre du téléchargement. (long)
* **responseCode** : le code de réponse HTTP retourné par le serveur. (long)
* **response** : la réponse HTTP renvoyée par le serveur. (DOMString)
* **en-têtes** : en-têtes de réponse HTTP par le serveur. (Objet)
* Actuellement pris en charge sur iOS seulement.
### iOS Remarques
* Ne prend pas en charge les propriétés `responseCode` et `bytesSent`.
## download
**Paramètres**:
* **source** : l'URL du serveur depuis lequel télécharger le fichier, encodée via `encodeURI()`.
* **target** : système de fichiers url représentant le fichier sur le périphérique. Pour vers l'arrière la compatibilité, cela peut aussi être le chemin d'accès complet du fichier sur le périphérique. (Voir [vers l'arrière compatibilité note] ci-dessous)
* **successCallback** : une callback de succès à laquelle est passée un objet `FileEntry`. *(Function)*
* **errorCallback**: un rappel qui s'exécute si une erreur se produit lors de la récupération du `FileEntry` . Appelée avec un `FileTransferError` objet. *(Fonction)*
* **trustAllHosts**: paramètre facultatif, valeur par défaut est `false` . Si la valeur est `true` , il accepte tous les certificats de sécurité. Ceci peut être utile car Android rejette les certificats auto-signés. N'est pas recommandé pour une utilisation en production. Supporté sur Android et iOS. *(booléen)*
* **options** : paramètres facultatifs, seules les en-têtes sont actuellement supportées (par exemple l'autorisation (authentification basique), etc.).
### Exemple
// !! Suppose fileURL variable contient une URL valide vers un chemin d'accès sur le périphérique, / / par exemple, transfert de fichiers var cdvfile://localhost/persistent/path/to/downloads/ = new FileTransfer() ;
var uri = encodeURI ("http://some.server.com/download.php") ;
fileTransfer.download (uri, fileURL, function(entry) {console.log ("téléchargement complet:" + entry.toURL()) ;
}, function(error) {console.log (« source de l'erreur de téléchargement » + error.source) ;
Console.log (« erreur de téléchargement cible » + error.target) ;
Console.log (« code d'erreur de téléchargement » + error.code) ;
}, faux, {en-têtes: {« Autorisation »: « dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA base == "}}) ;
## abort
Abandonne un transfert en cours. Le rappel onerror est passé à un objet FileTransferError qui a un code d'erreur de FileTransferError.ABORT_ERR.
### Exemple
// !! Suppose fileURL variable contient une URL valide dans un fichier texte sur le périphérique, / / par exemple, cdvfile://localhost/persistent/path/to/file.txt var win = function(r) {console.log ("ne devrait pas être appelée.");}
var fail = function(error) {/ / error.code == FileTransferError.ABORT_ERR alert ("une erreur est survenue : Code =" + error.code) ;
Console.log (« source de l'erreur de téléchargement » + error.source) ;
Console.log ("erreur de téléchargement cible" + error.target);}
options de var = new FileUploadOptions() ;
options.fileKey="file" ;
options.fileName="myphoto.jpg" ;
options.mimeType="image/jpeg" ;
ft var = new FileTransfer() ;
ft.upload (fileURL, encodeURI ("http://some.server.com/upload.php"), win, fail, options) ;
ft.Abort() ;
## FileTransferError
A `FileTransferError` objet est passé à un rappel d'erreur lorsqu'une erreur survient.
### Propriétés
* **code** : l'un des codes d'erreur prédéfinis énumérés ci-dessous. (Number)
* **source** : l'URI de la source. (String)
* **target**: l'URI de la destination. (String)
* **http_status** : code d'état HTTP. Cet attribut n'est disponible que lorsqu'un code de réponse est fourni via la connexion HTTP. (Number)
* **corps** Corps de réponse. Cet attribut n'est disponible que lorsqu'une réponse est reçue de la connexion HTTP. (String)
* **exception**: soit e.getMessage ou e.toString (String)
### Constantes
* 1 = `FileTransferError.FILE_NOT_FOUND_ERR`
* 2 = `FileTransferError.INVALID_URL_ERR`
* 3 = `FileTransferError.CONNECTION_ERR`
* 4 = `FileTransferError.ABORT_ERR`
* 5 = `FileTransferError.NOT_MODIFIED_ERR`
## Backwards Compatibility Notes
Les versions précédentes de ce plugin n'accepterait périphérique--fichier-chemins d'accès absolus comme source pour les téléchargements, ou comme cible pour les téléchargements. Ces chemins seraient généralement de la forme
/ var/mobile/Applications/< application UUID >/Documents/chemin/vers/fichier (iOS), /storage/emulated/0/path/to/file (Android)
Pour vers l'arrière la compatibilité, ces chemins sont toujours acceptés, et si votre application a enregistré des chemins comme celles-ci dans un stockage persistant, alors ils peuvent continuer à être utilisé.
Ces chemins ont été précédemment exposés dans le `fullPath` propriété de `FileEntry` et `DirectoryEntry` les objets retournés par le fichier plugin. Nouvelles versions du fichier plugin, cependant, ne plus exposent ces chemins à JavaScript.
Si vous migrez vers une nouvelle (1.0.0 ou plus récent) version de fichier et vous avez précédemment utilisé `entry.fullPath` comme arguments à `download()` ou `upload()` , alors vous aurez besoin de modifier votre code pour utiliser le système de fichiers URL au lieu de cela.
`FileEntry.toURL()`et `DirectoryEntry.toURL()` retournent une URL de système de fichiers du formulaire
cdvfile://localhost/persistent/path/to/file
qui peut être utilisé à la place le chemin d'accès absolu au fichier dans les deux `download()` et `upload()` méthodes.

View File

@@ -0,0 +1,311 @@
<!--
# license: Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-->
# cordova-plugin-file-transfer
[![Build Status](https://travis-ci.org/apache/cordova-plugin-file-transfer.svg)](https://travis-ci.org/apache/cordova-plugin-file-transfer)
Documentazione plugin: <doc/index.md>
Questo plugin permette di caricare e scaricare file.
Questo plugin definisce globale `FileTransfer`, costruttori di `FileUploadOptions`.
Anche se in ambito globale, non sono disponibili fino a dopo l'evento `deviceready`.
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
console.log(FileTransfer);
}
## Installazione
cordova plugin add cordova-plugin-file-transfer
## Piattaforme supportate
* Amazon fuoco OS
* Android
* BlackBerry 10
* Browser
* Firefox OS**
* iOS
* Windows Phone 7 e 8 *
* Windows 8
* Windows
\ * *Non supportano `onprogress` né `abort()` *
\ * * *Non supportano `onprogress` *
# FileTransfer
L'oggetto `FileTransfer` fornisce un modo per caricare i file utilizzando una richiesta HTTP multiparte POST o PUT e scaricare file pure.
## Proprietà
* **OnProgress**: chiamata con un `ProgressEvent` ogni volta che un nuovo blocco di dati viene trasferito. *(Funzione)*
## Metodi
* **caricare**: invia un file a un server.
* **Scarica**: Scarica un file dal server.
* **Abort**: interrompe un trasferimento in corso.
## upload
**Parametri**:
* **fileURL**: Filesystem URL che rappresenta il file nel dispositivo. Per indietro la compatibilità, questo può anche essere il percorso completo del file sul dispositivo. (Vedere [indietro compatibilità rileva] qui sotto)
* **server**: URL del server per ricevere il file, come codificato dal`encodeURI()`.
* **successCallback**: un callback che viene passato un oggetto `FileUploadResult`. *(Funzione)*
* **errorCallback**: un callback che viene eseguito se si verifica un errore di recupero `FileUploadResult`. Richiamato con un oggetto `FileTransferError`. *(Funzione)*
* **opzioni**: parametri facoltativi *(oggetto)*. Chiavi valide:
* **fileKey**: il nome dell'elemento form. Valore predefinito è `file` . (DOMString)
* **nome file**: il nome del file da utilizzare quando si salva il file sul server. Valore predefinito è `image.jpg` . (DOMString)
* **httpMethod**: metodo HTTP da utilizzare - `PUT` o `POST`. Impostazioni predefinite per `POST`. (DOMString)
* **mimeType**: il tipo mime dei dati da caricare. Impostazioni predefinite su `image/jpeg`. (DOMString)
* **params**: un insieme di coppie chiave/valore opzionale per passare nella richiesta HTTP. (Object)
* **chunkedMode**: se a caricare i dati in modalità streaming chunked. Impostazione predefinita è `true`. (Boolean)
* **headers**: una mappa di valori di intestazione e nome dell'intestazione. Utilizzare una matrice per specificare più di un valore. Su iOS, FireOS e Android, se è presente, un'intestazione Content-Type il nome dati form multipart non verranno utilizzati. (Object)
* **httpMethod**: metodo HTTP da utilizzare per esempio POST o PUT. Il valore predefinito è `POST`. (DOMString)
* **trustAllHosts**: parametro opzionale, valore predefinito è `false` . Se impostata su `true` , accetta tutti i certificati di sicurezza. Questo è utile poiché Android respinge i certificati autofirmati sicurezza. Non raccomandato per uso in produzione. Supportato su Android e iOS. *(boolean)*
### Esempio
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function (r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
var fail = function (error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
options.mimeType = "text/plain";
var params = {};
params.value1 = "test";
params.value2 = "param";
options.params = params;
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
### Esempio con intestazioni di caricare ed eventi Progress (Android e iOS solo)
function win(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
function fail(error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var uri = encodeURI("http://some.server.com/upload.php");
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1);
options.mimeType="text/plain";
var headers={'headerParam':'headerValue'};
options.headers = headers;
var ft = new FileTransfer();
ft.onprogress = function(progressEvent) {
if (progressEvent.lengthComputable) {
loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
} else {
loadingStatus.increment();
}
};
ft.upload(fileURL, uri, win, fail, options);
## FileUploadResult
Un oggetto `FileUploadResult` viene passato al metodo di callback del metodo `upload()` dell'oggetto `FileTransfer` successo.
### Proprietà
* **bytesSent**: il numero di byte inviati al server come parte dell'upload. (lungo)
* **responseCode**: codice di risposta HTTP restituito dal server. (lungo)
* **risposta**: risposta HTTP restituito dal server. (DOMString)
* **intestazioni**: intestazioni di risposta HTTP dal server. (Oggetto)
* Attualmente supportato solo iOS.
### iOS stranezze
* Non supporta `responseCode` o`bytesSent`.
## Scarica
**Parametri**:
* **fonte**: URL del server per scaricare il file, come codificato dal`encodeURI()`.
* **destinazione**: Filesystem url che rappresenta il file nel dispositivo. Per indietro la compatibilità, questo può anche essere il percorso completo del file sul dispositivo. (Vedere [indietro compatibilità rileva] qui sotto)
* **successCallback**: un callback passato un `FileEntry` oggetto. *(Funzione)*
* **errorCallback**: un callback che viene eseguito se si verifica un errore durante il recupero `FileEntry`. Richiamato con un oggetto `FileTransferError`. *(Function)*
* **trustAllHosts**: parametro opzionale, valore predefinito è `false` . Se impostata su `true` , accetta tutti i certificati di sicurezza. Questo è utile perché Android respinge i certificati autofirmati sicurezza. Non raccomandato per uso in produzione. Supportato su Android e iOS. *(boolean)*
* **opzioni**: parametri facoltativi, attualmente solo supporti intestazioni (ad esempio autorizzazione (autenticazione di base), ecc.).
### Esempio
// !! Assumes variable fileURL contains a valid URL to a path on the device,
// for example, cdvfile://localhost/persistent/path/to/downloads/
var fileTransfer = new FileTransfer();
var uri = encodeURI("http://some.server.com/download.php");
fileTransfer.download(
uri,
fileURL,
function(entry) {
console.log("download complete: " + entry.toURL());
},
function(error) {
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("upload error code" + error.code);
},
false,
{
headers: {
"Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
}
}
);
### WP8 stranezze
* Il download richiede è nella cache di implementazione nativa. Per evitare la memorizzazione nella cache, passare `if-Modified-Since` intestazione per metodo di download.
## Abort
Interrompe un trasferimento in corso. Il callback onerror viene passato un oggetto FileTransferError che presenta un codice di errore di FileTransferError.ABORT_ERR.
### Esempio
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function(r) {
console.log("Should not be called.");
}
var fail = function(error) {
// error.code == FileTransferError.ABORT_ERR
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName="myphoto.jpg";
options.mimeType="image/jpeg";
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
ft.abort();
## FileTransferError
Un oggetto `FileTransferError` viene passato a un callback di errore quando si verifica un errore.
### Proprietà
* **codice**: uno dei codici di errore predefiniti elencati di seguito. (Numero)
* **fonte**: URL all'origine. (String)
* **destinazione**: URL di destinazione. (String)
* **http_status**: codice di stato HTTP. Questo attributo è disponibile solo quando viene ricevuto un codice di risposta della connessione HTTP. (Numero)
* **body** Corpo della risposta. Questo attributo è disponibile solo quando viene ricevuta una risposta dalla connessione HTTP. (String)
* **exception**: O e.getMessage o e.toString (String)
### Costanti
* 1 = `FileTransferError.FILE_NOT_FOUND_ERR`
* 2 = `FileTransferError.INVALID_URL_ERR`
* 3 = `FileTransferError.CONNECTION_ERR`
* 4 = `FileTransferError.ABORT_ERR`
* 5 = `FileTransferError.NOT_MODIFIED_ERR`
## Note di compatibilità all'indietro
Versioni precedenti di questo plugin accetterebbe solo dispositivo-assoluto-percorsi di file come origine per upload, o come destinazione per il download. Questi percorsi si sarebbero generalmente di forma
/var/mobile/Applications/<application UUID>/Documents/path/to/file (iOS)
/storage/emulated/0/path/to/file (Android)
Per indietro compatibilità, questi percorsi sono ancora accettati, e se l'applicazione ha registrato percorsi come questi in un archivio permanente, quindi possono continuare a essere utilizzato.
Questi percorsi sono stati precedentemente esposti nella proprietà `fullPath` di `FileEntry` e oggetti `DirectoryEntry` restituiti dal File plugin. Nuove versioni del File plugin, tuttavia, non è più espongono questi percorsi a JavaScript.
Se si esegue l'aggiornamento a una nuova (1.0.0 o più recente) versione del File e si hanno precedentemente utilizzato `entry.fullPath` come argomenti per `download()` o `upload()`, quindi sarà necessario cambiare il codice per utilizzare gli URL filesystem invece.
`FileEntry.toURL()` e `DirectoryEntry.toURL()` restituiscono un filesystem URL del modulo
cdvfile://localhost/persistent/path/to/file
che può essere utilizzato al posto del percorso assoluto nei metodi sia `download()` e `upload()`.

View File

@@ -0,0 +1,302 @@
<!---
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
# cordova-plugin-file-transfer
Questo plugin permette di caricare e scaricare file.
Questo plugin definisce globale `FileTransfer`, costruttori di `FileUploadOptions`.
Anche se in ambito globale, non sono disponibili fino a dopo l'evento `deviceready`.
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
console.log(FileTransfer);
}
## Installazione
cordova plugin add cordova-plugin-file-transfer
## Piattaforme supportate
* Amazon fuoco OS
* Android
* BlackBerry 10
* Browser
* Firefox OS**
* iOS
* Windows Phone 7 e 8 *
* Windows 8
* Windows
* *Supporto `onprogress``abort()`*
** *Non supportano `onprogress`*
# FileTransfer
L'oggetto `FileTransfer` fornisce un modo per caricare i file utilizzando una richiesta HTTP di POST più parte e scaricare file pure.
## Proprietà
* **OnProgress**: chiamata con un `ProgressEvent` ogni volta che un nuovo blocco di dati viene trasferito. *(Funzione)*
## Metodi
* **caricare**: invia un file a un server.
* **Scarica**: Scarica un file dal server.
* **Abort**: interrompe un trasferimento in corso.
## caricare
**Parametri**:
* **fileURL**: Filesystem URL che rappresenta il file nel dispositivo. Per indietro la compatibilità, questo può anche essere il percorso completo del file sul dispositivo. (Vedere [indietro compatibilità rileva] qui sotto)
* **server**: URL del server per ricevere il file, come codificato dal`encodeURI()`.
* **successCallback**: un callback che viene passato un oggetto `FileUploadResult`. *(Funzione)*
* **errorCallback**: un callback che viene eseguito se si verifica un errore di recupero `FileUploadResult`. Richiamato con un oggetto `FileTransferError`. *(Funzione)*
* **opzioni**: parametri facoltativi *(oggetto)*. Chiavi valide:
* **fileKey**: il nome dell'elemento form. Valore predefinito è `file` . (DOMString)
* **nome file**: il nome del file da utilizzare quando si salva il file sul server. Valore predefinito è `image.jpg` . (DOMString)
* **httpMethod**: metodo HTTP da utilizzare - `PUT` o `POST`. Impostazioni predefinite per `POST`. (DOMString)
* **mimeType**: il tipo mime dei dati da caricare. Impostazioni predefinite su `image/jpeg`. (DOMString)
* **params**: un insieme di coppie chiave/valore opzionale per passare nella richiesta HTTP. (Object)
* **chunkedMode**: se a caricare i dati in modalità streaming chunked. Impostazione predefinita è `true`. (Boolean)
* **headers**: mappa di valori nome/intestazione intestazione. Utilizzare una matrice per specificare più valori. (Object)
* **trustAllHosts**: parametro opzionale, valore predefinito è `false` . Se impostata su `true` , accetta tutti i certificati di sicurezza. Questo è utile poiché Android respinge i certificati autofirmati sicurezza. Non raccomandato per uso in produzione. Supportato su Android e iOS. *(boolean)*
### Esempio
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function (r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
var fail = function (error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
options.mimeType = "text/plain";
var params = {};
params.value1 = "test";
params.value2 = "param";
options.params = params;
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
### Esempio con intestazioni di caricare ed eventi Progress (Android e iOS solo)
function win(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
function fail(error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var uri = encodeURI("http://some.server.com/upload.php");
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1);
options.mimeType="text/plain";
var headers={'headerParam':'headerValue'};
options.headers = headers;
var ft = new FileTransfer();
ft.onprogress = function(progressEvent) {
if (progressEvent.lengthComputable) {
loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
} else {
loadingStatus.increment();
}
};
ft.upload(fileURL, uri, win, fail, options);
## FileUploadResult
Un oggetto `FileUploadResult` viene passato al metodo di callback del metodo `upload()` dell'oggetto `FileTransfer` successo.
### Proprietà
* **bytesSent**: il numero di byte inviati al server come parte dell'upload. (lungo)
* **responseCode**: codice di risposta HTTP restituito dal server. (lungo)
* **risposta**: risposta HTTP restituito dal server. (DOMString)
* **intestazioni**: intestazioni di risposta HTTP dal server. (Oggetto)
* Attualmente supportato solo iOS.
### iOS stranezze
* Non supporta `responseCode` o`bytesSent`.
## Scarica
**Parametri**:
* **fonte**: URL del server per scaricare il file, come codificato dal`encodeURI()`.
* **destinazione**: Filesystem url che rappresenta il file nel dispositivo. Per indietro la compatibilità, questo può anche essere il percorso completo del file sul dispositivo. (Vedere [indietro compatibilità rileva] qui sotto)
* **successCallback**: un callback passato un `FileEntry` oggetto. *(Funzione)*
* **errorCallback**: un callback che viene eseguito se si verifica un errore durante il recupero `FileEntry`. Richiamato con un oggetto `FileTransferError`. *(Function)*
* **trustAllHosts**: parametro opzionale, valore predefinito è `false` . Se impostata su `true` , accetta tutti i certificati di sicurezza. Questo è utile perché Android respinge i certificati autofirmati sicurezza. Non raccomandato per uso in produzione. Supportato su Android e iOS. *(boolean)*
* **opzioni**: parametri facoltativi, attualmente solo supporti intestazioni (ad esempio autorizzazione (autenticazione di base), ecc.).
### Esempio
// !! Assumes variable fileURL contains a valid URL to a path on the device,
// for example, cdvfile://localhost/persistent/path/to/downloads/
var fileTransfer = new FileTransfer();
var uri = encodeURI("http://some.server.com/download.php");
fileTransfer.download(
uri,
fileURL,
function(entry) {
console.log("download complete: " + entry.toURL());
},
function(error) {
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("upload error code" + error.code);
},
false,
{
headers: {
"Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
}
}
);
## Abort
Interrompe un trasferimento in corso. Il callback onerror viene passato un oggetto FileTransferError che presenta un codice di errore di FileTransferError.ABORT_ERR.
### Esempio
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function(r) {
console.log("Should not be called.");
}
var fail = function(error) {
// error.code == FileTransferError.ABORT_ERR
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName="myphoto.jpg";
options.mimeType="image/jpeg";
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
ft.abort();
## FileTransferError
Un oggetto `FileTransferError` viene passato a un callback di errore quando si verifica un errore.
### Proprietà
* **codice**: uno dei codici di errore predefiniti elencati di seguito. (Numero)
* **fonte**: URL all'origine. (String)
* **destinazione**: URL di destinazione. (String)
* **http_status**: codice di stato HTTP. Questo attributo è disponibile solo quando viene ricevuto un codice di risposta della connessione HTTP. (Numero)
* **body** Corpo della risposta. Questo attributo è disponibile solo quando viene ricevuta una risposta dalla connessione HTTP. (String)
* **exception**: O e.getMessage o e.toString (String)
### Costanti
* 1 = `FileTransferError.FILE_NOT_FOUND_ERR`
* 2 = `FileTransferError.INVALID_URL_ERR`
* 3 = `FileTransferError.CONNECTION_ERR`
* 4 = `FileTransferError.ABORT_ERR`
* 5 = `FileTransferError.NOT_MODIFIED_ERR`
## Note di compatibilità all'indietro
Versioni precedenti di questo plugin accetterebbe solo dispositivo-assoluto-percorsi di file come origine per upload, o come destinazione per il download. Questi percorsi si sarebbero generalmente di forma
/var/mobile/Applications/<application UUID>/Documents/path/to/file (iOS)
/storage/emulated/0/path/to/file (Android)
Per indietro compatibilità, questi percorsi sono ancora accettati, e se l'applicazione ha registrato percorsi come questi in un archivio permanente, quindi possono continuare a essere utilizzato.
Questi percorsi sono stati precedentemente esposti nella proprietà `fullPath` di `FileEntry` e oggetti `DirectoryEntry` restituiti dal File plugin. Nuove versioni del File plugin, tuttavia, non è più espongono questi percorsi a JavaScript.
Se si esegue l'aggiornamento a una nuova (1.0.0 o più recente) versione del File e si hanno precedentemente utilizzato `entry.fullPath` come argomenti per `download()` o `upload()`, quindi sarà necessario cambiare il codice per utilizzare gli URL filesystem invece.
`FileEntry.toURL()` e `DirectoryEntry.toURL()` restituiscono un filesystem URL del modulo
cdvfile://localhost/persistent/path/to/file
che può essere utilizzato al posto del percorso assoluto nei metodi sia `download()` e `upload()`.

View File

@@ -0,0 +1,311 @@
<!--
# license: Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-->
# cordova-plugin-file-transfer
[![Build Status](https://travis-ci.org/apache/cordova-plugin-file-transfer.svg)](https://travis-ci.org/apache/cordova-plugin-file-transfer)
プラグインのマニュアル: <doc/index.md>
このプラグインは、アップロードし、ファイルをダウンロードすることができます。
このプラグインでは、グローバル `FileTransfer``FileUploadOptions` コンス トラクターを定義します。
グローバル スコープでは使用できませんまで `deviceready` イベントの後です。
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
console.log(FileTransfer);
}
## インストール
cordova plugin add cordova-plugin-file-transfer
## サポートされているプラットフォーム
* アマゾン火 OS
* アンドロイド
* ブラックベリー 10
* ブラウザー
* Firefox の OS * *
* iOS
* Windows Phone 7 と 8 *
* Windows 8
* Windows
\ * * `Onprogress`も`abort()`をサポートしていません。*
\ * * * `Onprogress`をサポートしていません。*
# 出色
`出色`オブジェクトは、HTTP マルチパート POST または PUT 要求を使用してファイルをアップロードし、同様にファイルをダウンロードする方法を提供します。
## プロパティ
* **onprogress**: と呼ばれる、 `ProgressEvent` データの新しいチャンクが転送されるたびに。*(機能)*
## メソッド
* **アップロード**: サーバーにファイルを送信します。
* **ダウンロード**: サーバーからファイルをダウンロードします。
* **中止**: 進行中の転送を中止します。
## upload
**パラメーター**:
* **fileURL**: デバイス上のファイルを表すファイルシステム URL。 下位互換性は、このことも、デバイス上のファイルの完全パスであります。 (参照してください [後方互換性メモ] の下)
* **サーバー**: によって符号化されるように、ファイルを受信するサーバーの URL`encodeURI()`.
* **successCallback**: `FileUploadResult` オブジェクトが渡されるコールバック。*(機能)*
* **errorCallback**: エラー `FileUploadResult` を取得するが発生した場合に実行されるコールバック。`FileTransferError` オブジェクトを使って呼び出されます。*(機能)*
* **オプション**: 省略可能なパラメーター *(オブジェクト)*。有効なキー:
* **fileKey**: フォーム要素の名前。既定値は `file` です。(,)
* **ファイル名** ファイル名、サーバー上のファイルを保存するときに使用します。既定値は `image.jpg` です。(,)
* **httpMethod**: - `を置く` または `POST` のいずれかを使用する HTTP メソッド。デフォルト `のポスト` です。(,)
* **mimeType**: アップロードするデータの mime タイプ。`イメージ/jpeg` のデフォルトです。(,)
* **params**: HTTP リクエストに渡すために任意のキー/値ペアのセット。(オブジェクト)
* **chunkedMode**: チャンク ストリーミング モードでデータをアップロードするかどうか。デフォルトは `true` です。(ブール値)
* **headers**: ヘッダー名/ヘッダー値のマップ。 配列を使用して、1 つ以上の値を指定します。 IOS、FireOS、アンドロイドではという名前のコンテンツ タイプ ヘッダーが存在する場合、マルチパート フォーム データは使用されません。 (Object)
* **httpMethod**: 例えばを使用する HTTP メソッドを POST または PUT です。 デフォルト`のポスト`です。(DOMString)
* **trustAllHosts**: 省略可能なパラメーターは、デフォルト `false` 。 場合設定 `true` 、セキュリティ証明書をすべて受け付けます。 これは Android の自己署名入りセキュリティ証明書を拒否するので便利です。 運用環境で使用しないでください。 Android と iOS でサポートされています。 *(ブール値)*
### 例
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function (r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
var fail = function (error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
options.mimeType = "text/plain";
var params = {};
params.value1 = "test";
params.value2 = "param";
options.params = params;
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
### サンプルのアップロード ヘッダーと進行状況のイベント Android と iOS のみ)
function win(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
function fail(error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var uri = encodeURI("http://some.server.com/upload.php");
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1);
options.mimeType="text/plain";
var headers={'headerParam':'headerValue'};
options.headers = headers;
var ft = new FileTransfer();
ft.onprogress = function(progressEvent) {
if (progressEvent.lengthComputable) {
loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
} else {
loadingStatus.increment();
}
};
ft.upload(fileURL, uri, win, fail, options);
## FileUploadResult
`FileUploadResult` オブジェクトは `FileTransfer` オブジェクト `upload()` メソッドの成功時のコールバックに渡されます。
### プロパティ
* **bytesSent**: アップロードの一部としてサーバーに送信されたバイト数。(ロング)
* **記述**: サーバーによって返される HTTP 応答コード。(ロング)
* **応答**: サーバーによって返される HTTP 応答。(,)
* **ヘッダー**: HTTP 応答ヘッダー サーバーによって。(オブジェクト)
* 現在 iOS のみでサポートされます。
### iOS の癖
* サポートしていない `responseCode` または`bytesSent`.
## download
**パラメーター**:
* **ソース**: によって符号化されるように、ファイルをダウンロードするサーバーの URL`encodeURI()`.
* **ターゲット**: デバイス上のファイルを表すファイルシステム url。 下位互換性は、このことも、デバイス上のファイルの完全パスであります。 (参照してください [後方互換性メモ] の下)
* **successCallback**: 渡されたコールバックを `FileEntry` オブジェクト。*(機能)*
* **errorCallback**: `FileEntry` を取得するときにエラーが発生した場合に実行されるコールバック。`FileTransferError` オブジェクトを使って呼び出されます。*(機能)*
* **trustAllHosts**: 省略可能なパラメーターは、デフォルト `false` 。 場合設定 `true` 、セキュリティ証明書をすべて受け付けます。 Android は、自己署名入りセキュリティ証明書を拒否しますので便利です。 運用環境で使用しないでください。 Android と iOS でサポートされています。 *(ブール値)*
* **オプション**: 省略可能なパラメーターは、現在サポートするヘッダーのみ (認証 (基本認証) など)。
### 例
// !! Assumes variable fileURL contains a valid URL to a path on the device,
// for example, cdvfile://localhost/persistent/path/to/downloads/
var fileTransfer = new FileTransfer();
var uri = encodeURI("http://some.server.com/download.php");
fileTransfer.download(
uri,
fileURL,
function(entry) {
console.log("download complete: " + entry.toURL());
},
function(error) {
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("upload error code" + error.code);
},
false,
{
headers: {
"Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
}
}
);
### WP8 癖
* ダウンロード要求するネイティブ実装によってキャッシュに格納されています。キャッシュされないように、渡す`以来変更された if`ヘッダー メソッドをダウンロードします。
## abort
進行中の転送を中止します。Onerror コールバックが FileTransferError.ABORT_ERR のエラー コードを持っている FileTransferError オブジェクトに渡されます。
### 例
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function(r) {
console.log("Should not be called.");
}
var fail = function(error) {
// error.code == FileTransferError.ABORT_ERR
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName="myphoto.jpg";
options.mimeType="image/jpeg";
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
ft.abort();
## FileTransferError
`FileTransferError` オブジェクトは、エラーが発生したときにエラー コールバックに渡されます。
### プロパティ
* **コード**: 次のいずれかの定義済みのエラー コード。(数)
* **ソース**: ソースの URL。(文字列)
* **ターゲット**: 先の URL。(文字列)
* **http_status**: HTTP ステータス コード。この属性は、HTTP 接続から応答コードを受信したときにのみ使用できます。(数)
* **body**応答本体。この属性は、HTTP 接続から応答を受信したときにのみ使用できます。(文字列)
* **exception**: どちらか e.getMessage または e.toString (文字列)
### 定数
* 1 = `FileTransferError.FILE_NOT_FOUND_ERR`
* 2 = `FileTransferError.INVALID_URL_ERR`
* 3 = `FileTransferError.CONNECTION_ERR`
* 4 = `FileTransferError.ABORT_ERR`
* 5 = `FileTransferError.NOT_MODIFIED_ERR`
## 後方互換性をノートします。
このプラグインの以前のバージョンまたはダウンロードのターゲットとして、アップロードのソースとしてのみデバイス絶対ファイル パスを受け入れるでしょう。これらのパスの形式は、通常
/var/mobile/Applications/<application UUID>/Documents/path/to/file (iOS)
/storage/emulated/0/path/to/file (Android)
下位互換性、これらのパスを使用しても、アプリケーションは、永続的なストレージでこのようなパスを記録している場合、し彼らが引き続き使用されます。
これらのパスの `FileEntry` やファイル プラグインによって返される `DirectoryEntry` オブジェクトの `fullPath` プロパティで公開されていなかった。 新しいプラグインのバージョン、ファイル、ただし、もはや java スクリプトの設定をこれらのパスを公開します。
新しいにアップグレードする場合 (1.0.0 以降) ファイルのバージョン以前を使用している `entry.fullPath` `download()` または `upload()` への引数として、ファイルシステムの Url を代わりに使用するコードを変更する必要があります。
`FileEntry.toURL()``DirectoryEntry.toURL()` ファイルシステムの URL を返すフォーム
cdvfile://localhost/persistent/path/to/file
`download()``upload()` メソッドの絶対ファイル パスの代わりに使用できます。

View File

@@ -0,0 +1,302 @@
<!---
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
# cordova-plugin-file-transfer
このプラグインは、アップロードし、ファイルをダウンロードすることができます。
このプラグインでは、グローバル `FileTransfer``FileUploadOptions` コンス トラクターを定義します。
グローバル スコープでは使用できませんまで `deviceready` イベントの後です。
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
console.log(FileTransfer);
}
## インストール
cordova plugin add cordova-plugin-file-transfer
## サポートされているプラットフォーム
* アマゾン火 OS
* アンドロイド
* ブラックベリー 10
* ブラウザー
* Firefox の OS * *
* iOS
* Windows Phone 7 と 8 *
* Windows 8
* Windows
* *`onprogress``abort()` をサポートしていません*
* * *`onprogress` をサポートしていません*
# FileTransfer
`FileTransfer` オブジェクトはマルチパートのポスト、HTTP 要求を使用してファイルをアップロードして同様にファイルをダウンロードする方法を提供します。
## プロパティ
* **onprogress**: と呼ばれる、 `ProgressEvent` データの新しいチャンクが転送されるたびに。*(機能)*
## メソッド
* **アップロード**: サーバーにファイルを送信します。
* **ダウンロード**: サーバーからファイルをダウンロードします。
* **中止**: 進行中の転送を中止します。
## upload
**パラメーター**:
* **fileURL**: デバイス上のファイルを表すファイルシステム URL。 下位互換性は、このことも、デバイス上のファイルの完全パスであります。 (参照してください [後方互換性メモ] の下)
* **サーバー**: によって符号化されるように、ファイルを受信するサーバーの URL`encodeURI()`.
* **successCallback**: `FileUploadResult` オブジェクトが渡されるコールバック。*(機能)*
* **errorCallback**: エラー `FileUploadResult` を取得するが発生した場合に実行されるコールバック。`FileTransferError` オブジェクトを使って呼び出されます。*(機能)*
* **オプション**: 省略可能なパラメーター *(オブジェクト)*。有効なキー:
* **fileKey**: フォーム要素の名前。既定値は `file` です。(,)
* **ファイル名** ファイル名、サーバー上のファイルを保存するときに使用します。既定値は `image.jpg` です。(,)
* **httpMethod**: - `を置く` または `POST` のいずれかを使用する HTTP メソッド。デフォルト `のポスト` です。(,)
* **mimeType**: アップロードするデータの mime タイプ。`イメージ/jpeg` のデフォルトです。(,)
* **params**: HTTP リクエストに渡すために任意のキー/値ペアのセット。(オブジェクト)
* **chunkedMode**: チャンク ストリーミング モードでデータをアップロードするかどうか。デフォルトは `true` です。(ブール値)
* **headers**: ヘッダーの名前/ヘッダー値のマップ。1 つ以上の値を指定するには、配列を使用します。(オブジェクト)
* **trustAllHosts**: 省略可能なパラメーターは、デフォルト `false` 。 場合設定 `true` 、セキュリティ証明書をすべて受け付けます。 これは Android の自己署名入りセキュリティ証明書を拒否するので便利です。 運用環境で使用しないでください。 Android と iOS でサポートされています。 *(ブール値)*
### 例
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function (r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
var fail = function (error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
options.mimeType = "text/plain";
var params = {};
params.value1 = "test";
params.value2 = "param";
options.params = params;
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
### サンプルのアップロード ヘッダーと進行状況のイベント Android と iOS のみ)
function win(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
function fail(error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var uri = encodeURI("http://some.server.com/upload.php");
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1);
options.mimeType="text/plain";
var headers={'headerParam':'headerValue'};
options.headers = headers;
var ft = new FileTransfer();
ft.onprogress = function(progressEvent) {
if (progressEvent.lengthComputable) {
loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
} else {
loadingStatus.increment();
}
};
ft.upload(fileURL, uri, win, fail, options);
## FileUploadResult
`FileUploadResult` オブジェクトは `FileTransfer` オブジェクト `upload()` メソッドの成功時のコールバックに渡されます。
### プロパティ
* **bytesSent**: アップロードの一部としてサーバーに送信されたバイト数。(ロング)
* **記述**: サーバーによって返される HTTP 応答コード。(ロング)
* **応答**: サーバーによって返される HTTP 応答。(,)
* **ヘッダー**: HTTP 応答ヘッダー サーバーによって。(オブジェクト)
* 現在 iOS のみでサポートされます。
### iOS の癖
* サポートしていない `responseCode` または`bytesSent`.
## download
**パラメーター**:
* **ソース**: によって符号化されるように、ファイルをダウンロードするサーバーの URL`encodeURI()`.
* **ターゲット**: デバイス上のファイルを表すファイルシステム url。 下位互換性は、このことも、デバイス上のファイルの完全パスであります。 (参照してください [後方互換性メモ] の下)
* **successCallback**: 渡されたコールバックを `FileEntry` オブジェクト。*(機能)*
* **errorCallback**: `FileEntry` を取得するときにエラーが発生した場合に実行されるコールバック。`FileTransferError` オブジェクトを使って呼び出されます。*(機能)*
* **trustAllHosts**: 省略可能なパラメーターは、デフォルト `false` 。 場合設定 `true` 、セキュリティ証明書をすべて受け付けます。 Android は、自己署名入りセキュリティ証明書を拒否しますので便利です。 運用環境で使用しないでください。 Android と iOS でサポートされています。 *(ブール値)*
* **オプション**: 省略可能なパラメーターは、現在サポートするヘッダーのみ (認証 (基本認証) など)。
### 例
// !! Assumes variable fileURL contains a valid URL to a path on the device,
// for example, cdvfile://localhost/persistent/path/to/downloads/
var fileTransfer = new FileTransfer();
var uri = encodeURI("http://some.server.com/download.php");
fileTransfer.download(
uri,
fileURL,
function(entry) {
console.log("download complete: " + entry.toURL());
},
function(error) {
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("upload error code" + error.code);
},
false,
{
headers: {
"Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
}
}
);
## abort
進行中の転送を中止します。Onerror コールバックが FileTransferError.ABORT_ERR のエラー コードを持っている FileTransferError オブジェクトに渡されます。
### 例
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function(r) {
console.log("Should not be called.");
}
var fail = function(error) {
// error.code == FileTransferError.ABORT_ERR
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName="myphoto.jpg";
options.mimeType="image/jpeg";
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
ft.abort();
## FileTransferError
`FileTransferError` オブジェクトは、エラーが発生したときにエラー コールバックに渡されます。
### プロパティ
* **コード**: 次のいずれかの定義済みのエラー コード。(数)
* **ソース**: ソースの URL。(文字列)
* **ターゲット**: 先の URL。(文字列)
* **http_status**: HTTP ステータス コード。この属性は、HTTP 接続から応答コードを受信したときにのみ使用できます。(数)
* **body**応答本体。この属性は、HTTP 接続から応答を受信したときにのみ使用できます。(文字列)
* **exception**: どちらか e.getMessage または e.toString (文字列)
### 定数
* 1 = `FileTransferError.FILE_NOT_FOUND_ERR`
* 2 = `FileTransferError.INVALID_URL_ERR`
* 3 = `FileTransferError.CONNECTION_ERR`
* 4 = `FileTransferError.ABORT_ERR`
* 5 = `FileTransferError.NOT_MODIFIED_ERR`
## 後方互換性をノートします。
このプラグインの以前のバージョンまたはダウンロードのターゲットとして、アップロードのソースとしてのみデバイス絶対ファイル パスを受け入れるでしょう。これらのパスの形式は、通常
/var/mobile/Applications/<application UUID>/Documents/path/to/file (iOS)
/storage/emulated/0/path/to/file (Android)
下位互換性、これらのパスを使用しても、アプリケーションは、永続的なストレージでこのようなパスを記録している場合、し彼らが引き続き使用されます。
これらのパスの `FileEntry` やファイル プラグインによって返される `DirectoryEntry` オブジェクトの `fullPath` プロパティで公開されていなかった。 新しいプラグインのバージョン、ファイル、ただし、もはや java スクリプトの設定をこれらのパスを公開します。
新しいにアップグレードする場合 (1.0.0 以降) ファイルのバージョン以前を使用している `entry.fullPath` `download()` または `upload()` への引数として、ファイルシステムの Url を代わりに使用するコードを変更する必要があります。
`FileEntry.toURL()``DirectoryEntry.toURL()` ファイルシステムの URL を返すフォーム
cdvfile://localhost/persistent/path/to/file
`download()``upload()` メソッドの絶対ファイル パスの代わりに使用できます。

View File

@@ -0,0 +1,311 @@
<!--
# license: Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-->
# cordova-plugin-file-transfer
[![Build Status](https://travis-ci.org/apache/cordova-plugin-file-transfer.svg)](https://travis-ci.org/apache/cordova-plugin-file-transfer)
플러그인 문서: <doc/index.md>
이 플러그인을 사용 하면 업로드 및 다운로드 파일 수 있습니다.
이 플러그인 글로벌 `FileTransfer`, `FileUploadOptions` 생성자를 정의합니다.
전역 범위에서 그들은 제공 되지 않습니다 때까지 `deviceready` 이벤트 후.
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
console.log(FileTransfer);
}
## 설치
cordova plugin add cordova-plugin-file-transfer
## 지원 되는 플랫폼
* 아마존 화재 운영 체제
* 안 드 로이드
* 블랙베리 10
* 브라우저
* 파이어 폭스 OS * *
* iOS
* Windows Phone 7과 8 *
* 윈도우 8
* 윈도우
\** `Onprogress``abort()` 를 지원 하지 않습니다*
\*** `Onprogress` 를 지원 하지 않습니다*
# FileTransfer
`FileTransfer` 개체는 HTTP 다중 파트 POST 또는 PUT 요청을 사용 하 여 파일을 업로드 하 고 또한 파일을 다운로드 하는 방법을 제공 합니다.
## 속성
* **onprogress**:로 불리는 `ProgressEvent` 새로운 양의 데이터를 전송 하는 때마다. *(기능)*
## 메서드
* **업로드**: 파일을 서버에 보냅니다.
* **다운로드**: 서버에서 파일을 다운로드 합니다.
* **중단**: 진행 중인 전송 중단.
## 업로드
**매개 변수**:
* **fileURL**: 장치에 파일을 나타내는 파일 시스템 URL. 에 대 한 이전 버전과 호환성을이 수도 장치에 있는 파일의 전체 경로 수. (참조 [거꾸로 호환성 노트] 아래)
* **서버**: 인코딩 파일 수신 서버의 URL`encodeURI()`.
* **successCallback**: `FileUploadResult` 개체를 전달 하는 콜백. *(기능)*
* **errorCallback**: `FileUploadResult` 검색에 오류가 발생 하면 실행 되는 콜백. `FileTransferError` 개체와 함께 호출 됩니다. *(기능)*
* **옵션**: 선택적 매개 변수 *(개체)*. 유효한 키:
* **fileKey**: form 요소의 이름. 기본값은 `file` . (DOMString)
* **파일 이름**: 파일 이름을 서버에 파일을 저장할 때 사용 합니다. 기본값은 `image.jpg` . (DOMString)
* **httpMethod**: `넣어` 또는 `게시물`-사용 하도록 HTTP 메서드. `게시물` 기본값입니다. (DOMString)
* **mimeType**: 업로드 데이터의 mime 형식을. `이미지/jpeg`의 기본값입니다. (DOMString)
* **params**: HTTP 요청에 전달할 선택적 키/값 쌍의 집합. (개체)
* **chunkedMode**: 청크 스트리밍 모드에서 데이터 업로드를 합니다. 기본값은 `true`입니다. (부울)
* **headers**: 헤더 이름 및 헤더 값의 지도. 배열을 사용 하 여 하나 이상의 값을 지정. IOS, FireOS, 안 드 로이드에 있으면 라는 콘텐츠 형식 헤더, 다중 양식 데이터는 사용할 수 없습니다. (Object)
* **httpMethod**: HTTP 메서드 예를 사용 하 여 게시 하거나 넣어. `게시물`기본값입니다. (DOMString)
* **trustAllHosts**: 선택적 매개 변수는 기본적으로 `false` . 만약 설정 `true` , 그것은 모든 보안 인증서를 허용 합니다. 이 안 드 로이드 자체 서명 된 보안 인증서를 거부 하기 때문에 유용 합니다. 프로덕션 환경에서 사용 권장 되지 않습니다. 안 드 로이드와 iOS에서 지원. *(부울)*
### 예를 들어
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function (r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
var fail = function (error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
options.mimeType = "text/plain";
var params = {};
params.value1 = "test";
params.value2 = "param";
options.params = params;
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
### 예를 들어 헤더 업로드 및 진행 이벤트 (안 드 로이드와 iOS만)
function win(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
function fail(error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var uri = encodeURI("http://some.server.com/upload.php");
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1);
options.mimeType="text/plain";
var headers={'headerParam':'headerValue'};
options.headers = headers;
var ft = new FileTransfer();
ft.onprogress = function(progressEvent) {
if (progressEvent.lengthComputable) {
loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
} else {
loadingStatus.increment();
}
};
ft.upload(fileURL, uri, win, fail, options);
## FileUploadResult
`FileUploadResult` 개체 `FileTransfer` 개체의 `원래` 방법의 성공 콜백에 전달 됩니다.
### 속성
* **bytesSent**: 업로드의 일부로 서버에 보낸 바이트 수. (긴)
* **responseCode**: 서버에서 반환 된 HTTP 응답 코드. (긴)
* **response**: 서버에서 반환 되는 HTTP 응답. (DOMString)
* **headers**: 서버에서 HTTP 응답 헤더. (개체)
* 현재 ios만 지원 합니다.
### iOS 단점
* 지원 하지 않는 `responseCode` 또는`bytesSent`.
## download
**매개 변수**:
* **source**: URL로 인코딩된 파일, 다운로드 서버`encodeURI()`.
* **target**: 장치에 파일을 나타내는 파일 시스템 url. 에 대 한 이전 버전과 호환성을이 수도 장치에 있는 파일의 전체 경로 수. (참조 [거꾸로 호환성 노트] 아래)
* **successCallback**: 콜백 전달 되는 `FileEntry` 개체. *(기능)*
* **errorCallback**: `FileEntry`를 검색할 때 오류가 발생 하면 실행 되는 콜백. `FileTransferError` 개체와 함께 호출 됩니다. *(기능)*
* **trustAllHosts**: 선택적 매개 변수는 기본적으로 `false` . 만약 설정 `true` , 그것은 모든 보안 인증서를 허용 합니다. 안 드 로이드 자체 서명 된 보안 인증서를 거부 하기 때문에 유용 합니다. 프로덕션 환경에서 사용 권장 되지 않습니다. 안 드 로이드와 iOS에서 지원. *(부울)*
* **options**: 선택적 매개 변수를 현재 지 원하는 머리글만 (예: 인증 (기본 인증), 등).
### 예를 들어
// !! Assumes variable fileURL contains a valid URL to a path on the device,
// for example, cdvfile://localhost/persistent/path/to/downloads/
var fileTransfer = new FileTransfer();
var uri = encodeURI("http://some.server.com/download.php");
fileTransfer.download(
uri,
fileURL,
function(entry) {
console.log("download complete: " + entry.toURL());
},
function(error) {
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("upload error code" + error.code);
},
false,
{
headers: {
"Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
}
}
);
### WP8 특수
* 다운로드 요청 기본 구현에 의해 캐시 되 고. 캐싱을 방지 하려면 전달 `if-수정-이후` 헤더를 다운로드 하는 방법.
## abort
진행 중인 전송을 중단합니다. onerror 콜백 FileTransferError.ABORT_ERR의 오류 코드는 FileTransferError 개체를 전달 합니다.
### 예를 들어
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function(r) {
console.log("Should not be called.");
}
var fail = function(error) {
// error.code == FileTransferError.ABORT_ERR
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName="myphoto.jpg";
options.mimeType="image/jpeg";
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
ft.abort();
## FileTransferError
`FileTransferError` 개체 오류가 발생 하면 오류 콜백에 전달 됩니다.
### 속성
* **code**: 미리 정의 된 오류 코드 중 하나가 아래에 나열 된. (수)
* **source**: 소스 URL. (문자열)
* **target**: 대상 URL. (문자열)
* **http_status**: HTTP 상태 코드. 이 특성은 응답 코드를 HTTP 연결에서 수신에 사용할 수 있습니다. (수)
* **body** 응답 본문입니다. 이 특성은 HTTP 연결에서 응답을 받을 때에 사용할 수 있습니다. (문자열)
* **exception**: 어느 e.getMessage 또는 e.toString (문자열)
### 상수
* 1 = `FileTransferError.FILE_NOT_FOUND_ERR`
* 2 = `FileTransferError.INVALID_URL_ERR`
* 3 = `FileTransferError.CONNECTION_ERR`
* 4 = `FileTransferError.ABORT_ERR`
* 5 = `FileTransferError.NOT_MODIFIED_ERR`
## 이전 버전과 호환성 노트
이 플러그인의 이전 버전만 업로드에 대 한 소스 또는 다운로드에 대 한 대상 장치 절대 파일 경로 받아들일 것 이다. 이러한 경로 일반적으로 폼의 것
/var/mobile/Applications/<application UUID>/Documents/path/to/file (iOS)
/storage/emulated/0/path/to/file (Android)
대 한 뒤 호환성, 이러한 경로 여전히 허용, 그리고 응용 프로그램이 영구 저장소에서 이와 같은 경로 기록 했다, 그때 그들은 계속할 수 있다면 사용할 수.
이러한 경로 `FileEntry` 및 파일 플러그인에 의해 반환 된 `DirectoryEntry` 개체의 `fullPath` 속성에 노출 되었던. 그러나 파일 플러그인의,, 더 이상 새로운 버전 자바 스크립트이 경로 노출.
새로 업그레이드 하는 경우 (1.0.0 이상) 버전의 파일, 및 이전 사용 하 고 `entry.fullPath` `download()` 또는 `upload()` 인수로 다음 대신 파일 시스템 Url을 사용 하 여 코드를 변경 해야 합니다.
폼의 파일 URL을 반환 하는 `FileEntry.toURL()``DirectoryEntry.toURL()`
cdvfile://localhost/persistent/path/to/file
어떤 `download()``upload()` 방법에서 절대 파일 경로 대신 사용할 수 있습니다.

View File

@@ -0,0 +1,302 @@
<!---
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
# cordova-plugin-file-transfer
이 플러그인을 사용 하면 업로드 및 다운로드 파일 수 있습니다.
이 플러그인 글로벌 `FileTransfer`, `FileUploadOptions` 생성자를 정의합니다.
전역 범위에서 그들은 제공 되지 않습니다 때까지 `deviceready` 이벤트 후.
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
console.log(FileTransfer);
}
## 설치
cordova plugin add cordova-plugin-file-transfer
## 지원 되는 플랫폼
* 아마존 화재 운영 체제
* 안 드 로이드
* 블랙베리 10
* 브라우저
* 파이어 폭스 OS * *
* iOS
* Windows Phone 7과 8 *
* 윈도우 8
* 윈도우
* *`onprogress``abort()`를 지원 하지 않습니다*
* * *`onprogress`를 지원 하지 않습니다*
# FileTransfer
`FileTransfer` 개체는 HTTP 다중 파트 POST 요청을 사용 하 여 파일 업로드 뿐만 아니라 파일을 다운로드 하는 방법을 제공 합니다.
## 속성
* **onprogress**:로 불리는 `ProgressEvent` 새로운 양의 데이터를 전송 하는 때마다. *(기능)*
## 메서드
* **업로드**: 파일을 서버에 보냅니다.
* **다운로드**: 서버에서 파일을 다운로드 합니다.
* **중단**: 진행 중인 전송 중단.
## 업로드
**매개 변수**:
* **fileURL**: 장치에 파일을 나타내는 파일 시스템 URL. 에 대 한 이전 버전과 호환성을이 수도 장치에 있는 파일의 전체 경로 수. (참조 [거꾸로 호환성 노트] 아래)
* **서버**: 인코딩 파일 수신 서버의 URL`encodeURI()`.
* **successCallback**: `FileUploadResult` 개체를 전달 하는 콜백. *(기능)*
* **errorCallback**: `FileUploadResult` 검색에 오류가 발생 하면 실행 되는 콜백. `FileTransferError` 개체와 함께 호출 됩니다. *(기능)*
* **옵션**: 선택적 매개 변수 *(개체)*. 유효한 키:
* **fileKey**: form 요소의 이름. 기본값은 `file` . (DOMString)
* **파일 이름**: 파일 이름을 서버에 파일을 저장할 때 사용 합니다. 기본값은 `image.jpg` . (DOMString)
* **httpMethod**: `넣어` 또는 `게시물`-사용 하도록 HTTP 메서드. `게시물` 기본값입니다. (DOMString)
* **mimeType**: 업로드 데이터의 mime 형식을. `이미지/jpeg`의 기본값입니다. (DOMString)
* **params**: HTTP 요청에 전달할 선택적 키/값 쌍의 집합. (개체)
* **chunkedMode**: 청크 스트리밍 모드에서 데이터 업로드를 합니다. 기본값은 `true`입니다. (부울)
* **headers**: 헤더 이름/헤더 값의 지도. 배열을 사용 하 여 하나 이상의 값을 지정 합니다. (개체)
* **trustAllHosts**: 선택적 매개 변수는 기본적으로 `false` . 만약 설정 `true` , 그것은 모든 보안 인증서를 허용 합니다. 이 안 드 로이드 자체 서명 된 보안 인증서를 거부 하기 때문에 유용 합니다. 프로덕션 환경에서 사용 권장 되지 않습니다. 안 드 로이드와 iOS에서 지원. *(부울)*
### 예를 들어
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function (r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
var fail = function (error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
options.mimeType = "text/plain";
var params = {};
params.value1 = "test";
params.value2 = "param";
options.params = params;
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
### 예를 들어 헤더 업로드 및 진행 이벤트 (안 드 로이드와 iOS만)
function win(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
function fail(error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var uri = encodeURI("http://some.server.com/upload.php");
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1);
options.mimeType="text/plain";
var headers={'headerParam':'headerValue'};
options.headers = headers;
var ft = new FileTransfer();
ft.onprogress = function(progressEvent) {
if (progressEvent.lengthComputable) {
loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
} else {
loadingStatus.increment();
}
};
ft.upload(fileURL, uri, win, fail, options);
## FileUploadResult
`FileUploadResult` 개체 `FileTransfer` 개체의 `원래` 방법의 성공 콜백에 전달 됩니다.
### 속성
* **bytesSent**: 업로드의 일부로 서버에 보낸 바이트 수. (긴)
* **responseCode**: 서버에서 반환 된 HTTP 응답 코드. (긴)
* **response**: 서버에서 반환 되는 HTTP 응답. (DOMString)
* **headers**: 서버에서 HTTP 응답 헤더. (개체)
* 현재 ios만 지원 합니다.
### iOS 단점
* 지원 하지 않는 `responseCode` 또는`bytesSent`.
## download
**매개 변수**:
* **source**: URL로 인코딩된 파일, 다운로드 서버`encodeURI()`.
* **target**: 장치에 파일을 나타내는 파일 시스템 url. 에 대 한 이전 버전과 호환성을이 수도 장치에 있는 파일의 전체 경로 수. (참조 [거꾸로 호환성 노트] 아래)
* **successCallback**: 콜백 전달 되는 `FileEntry` 개체. *(기능)*
* **errorCallback**: `FileEntry`를 검색할 때 오류가 발생 하면 실행 되는 콜백. `FileTransferError` 개체와 함께 호출 됩니다. *(기능)*
* **trustAllHosts**: 선택적 매개 변수는 기본적으로 `false` . 만약 설정 `true` , 그것은 모든 보안 인증서를 허용 합니다. 안 드 로이드 자체 서명 된 보안 인증서를 거부 하기 때문에 유용 합니다. 프로덕션 환경에서 사용 권장 되지 않습니다. 안 드 로이드와 iOS에서 지원. *(부울)*
* **options**: 선택적 매개 변수를 현재 지 원하는 머리글만 (예: 인증 (기본 인증), 등).
### 예를 들어
// !! Assumes variable fileURL contains a valid URL to a path on the device,
// for example, cdvfile://localhost/persistent/path/to/downloads/
var fileTransfer = new FileTransfer();
var uri = encodeURI("http://some.server.com/download.php");
fileTransfer.download(
uri,
fileURL,
function(entry) {
console.log("download complete: " + entry.toURL());
},
function(error) {
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("upload error code" + error.code);
},
false,
{
headers: {
"Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
}
}
);
## abort
진행 중인 전송을 중단합니다. onerror 콜백 FileTransferError.ABORT_ERR의 오류 코드는 FileTransferError 개체를 전달 합니다.
### 예를 들어
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function(r) {
console.log("Should not be called.");
}
var fail = function(error) {
// error.code == FileTransferError.ABORT_ERR
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName="myphoto.jpg";
options.mimeType="image/jpeg";
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
ft.abort();
## FileTransferError
`FileTransferError` 개체 오류가 발생 하면 오류 콜백에 전달 됩니다.
### 속성
* **code**: 미리 정의 된 오류 코드 중 하나가 아래에 나열 된. (수)
* **source**: 소스 URL. (문자열)
* **target**: 대상 URL. (문자열)
* **http_status**: HTTP 상태 코드. 이 특성은 응답 코드를 HTTP 연결에서 수신에 사용할 수 있습니다. (수)
* **body** 응답 본문입니다. 이 특성은 HTTP 연결에서 응답을 받을 때에 사용할 수 있습니다. (문자열)
* **exception**: 어느 e.getMessage 또는 e.toString (문자열)
### 상수
* 1 = `FileTransferError.FILE_NOT_FOUND_ERR`
* 2 = `FileTransferError.INVALID_URL_ERR`
* 3 = `FileTransferError.CONNECTION_ERR`
* 4 = `FileTransferError.ABORT_ERR`
* 5 = `FileTransferError.NOT_MODIFIED_ERR`
## 이전 버전과 호환성 노트
이 플러그인의 이전 버전만 업로드에 대 한 소스 또는 다운로드에 대 한 대상 장치 절대 파일 경로 받아들일 것 이다. 이러한 경로 일반적으로 폼의 것
/var/mobile/Applications/<application UUID>/Documents/path/to/file (iOS)
/storage/emulated/0/path/to/file (Android)
대 한 뒤 호환성, 이러한 경로 여전히 허용, 그리고 응용 프로그램이 영구 저장소에서 이와 같은 경로 기록 했다, 그때 그들은 계속할 수 있다면 사용할 수.
이러한 경로 `FileEntry` 및 파일 플러그인에 의해 반환 된 `DirectoryEntry` 개체의 `fullPath` 속성에 노출 되었던. 그러나 파일 플러그인의,, 더 이상 새로운 버전 자바 스크립트이 경로 노출.
새로 업그레이드 하는 경우 (1.0.0 이상) 버전의 파일, 및 이전 사용 하 고 `entry.fullPath` `download()` 또는 `upload()` 인수로 다음 대신 파일 시스템 Url을 사용 하 여 코드를 변경 해야 합니다.
폼의 파일 URL을 반환 하는 `FileEntry.toURL()``DirectoryEntry.toURL()`
cdvfile://localhost/persistent/path/to/file
어떤 `download()``upload()` 방법에서 절대 파일 경로 대신 사용할 수 있습니다.

View File

@@ -0,0 +1,311 @@
<!--
# license: Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-->
# cordova-plugin-file-transfer
[![Build Status](https://travis-ci.org/apache/cordova-plugin-file-transfer.svg)](https://travis-ci.org/apache/cordova-plugin-file-transfer)
Plugin dokumentacja: <doc/index.md>
Plugin pozwala na przesyłanie i pobieranie plików.
Ten plugin określa globalne `FileTransfer`, `FileUploadOptions` konstruktorów.
Chociaż w globalnym zasięgu, są nie dostępne dopiero po `deviceready` imprezie.
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
console.log(FileTransfer);
}
## Instalacja
cordova plugin add cordova-plugin-file-transfer
## Obsługiwane platformy
* Amazon Fire OS
* Android
* BlackBerry 10
* Przeglądarka
* Firefox OS **
* iOS
* Windows Phone 7 i 8 *
* Windows 8
* Windows
\ * *Nie obsługują `onprogress` ani `abort()` *
\ ** *Nie obsługują `onprogress` *
# FileTransfer
Obiekt `FileTransfer` zapewnia sposób wgrać pliki za pomocą Multi-część POST lub PUT żądania HTTP i pobierania plików, jak również.
## Właściwości
* **OnProgress**: o nazwie `ProgressEvent` gdy nowy kawałek danych jest przenoszona. *(Funkcja)*
## Metody
* **wgraj**: wysyła plik na serwer.
* **do pobrania**: pliki do pobrania pliku z serwera.
* **przerwać**: przerywa w toku transferu.
## upload
**Parametry**:
* **fileURL**: URL plików reprezentujących pliku na urządzenie. Dla wstecznej kompatybilności, to może również być pełną ścieżkę pliku na urządzenie. (Zobacz [wstecz zgodności zauważa] poniżej)
* **serwer**: adres URL serwera, aby otrzymać plik, jak kodowane przez`encodeURI()`.
* **successCallback**: wywołania zwrotnego, który jest przekazywany obiekt `FileUploadResult`. *(Funkcja)*
* **errorCallback**: wywołanie zwrotne, które wykonuje, jeżeli wystąpi błąd pobierania `FileUploadResult`. Wywoływany z obiektu `FileTransferError`. *(Funkcja)*
* **Opcje**: parametry opcjonalne *(obiektu)*. Ważne klucze:
* **fileKey**: nazwa elementu form. Domyślnie `file` . (DOMString)
* **Nazwa pliku**: nazwy pliku, aby użyć podczas zapisywania pliku na serwerze. Domyślnie `image.jpg` . (DOMString)
* **element httpMethod**: Metoda HTTP do użycia - `umieścić` lub `POST`. Domyślnie `POST`. (DOMString)
* **mimeType**: Typ mime danych do przesłania. Domyślnie do `image/jpeg`. (DOMString)
* **params**: zestaw par opcjonalny klucz/wartość w żądaniu HTTP. (Obiekt)
* **chunkedMode**: czy przekazać dane w trybie pakietowego przesyłania strumieniowego. Wartością domyślną jest `true`. (Wartość logiczna)
* **headers**: Mapa wartości Nazwa/nagłówka nagłówek. Aby określić więcej niż jedną wartość, należy użyć tablicę. Na iOS, FireOS i Android jeśli nagłówek o nazwie Content-Type jest obecny, wieloczęściowa forma nie danych. (Object)
* **element httpMethod**: Metoda HTTP np. POST lub PUT. Ustawienia domyślne do `POST`. (DOMString)
* **trustAllHosts**: parametr opcjonalny, domyślnie `false` . Jeśli zestaw `true` , to akceptuje wszystkie certyfikaty bezpieczeństwa. Jest to przydatne, ponieważ Android odrzuca Certyfikaty samopodpisane. Nie zaleca się do użytku produkcyjnego. Obsługiwane na Androida i iOS. *(wartość logiczna)*
### Przykład
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function (r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
var fail = function (error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
options.mimeType = "text/plain";
var params = {};
params.value1 = "test";
params.value2 = "param";
options.params = params;
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
### Przykład z Prześlij nagłówki i zdarzeń postępu (Android i iOS tylko)
function win(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
function fail(error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var uri = encodeURI("http://some.server.com/upload.php");
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1);
options.mimeType="text/plain";
var headers={'headerParam':'headerValue'};
options.headers = headers;
var ft = new FileTransfer();
ft.onprogress = function(progressEvent) {
if (progressEvent.lengthComputable) {
loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
} else {
loadingStatus.increment();
}
};
ft.upload(fileURL, uri, win, fail, options);
## FileUploadResult
Obiekt `FileUploadResult` jest przekazywana do sukcesu wywołania zwrotnego metody `upload() służącą` obiektu `FileTransfer`.
### Właściwości
* **bytesSent**: liczba bajtów wysłanych do serwera jako część upload. (długie)
* **responseCode**: kod odpowiedzi HTTP, zwracane przez serwer. (długie)
* **odpowiedź**: HTTP odpowiedzi zwracane przez serwer. (DOMString)
* **nagłówki**: nagłówki HTTP odpowiedzi przez serwer. (Obiekt)
* Obecnie obsługiwane na iOS tylko.
### Dziwactwa iOS
* Nie obsługuje `responseCode` lub`bytesSent`.
## download
**Parametry**:
* **Źródło**: adres URL serwera, aby pobrać plik, jak kodowane przez`encodeURI()`.
* **cel**: url plików reprezentujących pliku na urządzenie. Dla wstecznej kompatybilności, to może również być pełną ścieżkę pliku na urządzenie. (Zobacz [wstecz zgodności zauważa] poniżej)
* **successCallback**: wywołania zwrotnego, który jest przekazywany `FileEntry` obiektu. *(Funkcja)*
* **errorCallback**: wywołanie zwrotne, które wykonuje, jeśli wystąpi błąd podczas pobierania `FileEntry`. Wywoływany z obiektu `FileTransferError`. *(Funkcja)*
* **trustAllHosts**: parametr opcjonalny, domyślnie `false` . Jeśli zestaw `true` , to akceptuje wszystkie certyfikaty bezpieczeństwa. Jest to przydatne, ponieważ Android odrzuca Certyfikaty samopodpisane. Nie zaleca się do użytku produkcyjnego. Obsługiwane na Androida i iOS. *(wartość logiczna)*
* **Opcje**: parametry opcjonalne, obecnie tylko obsługuje nagłówki (takie jak autoryzacja (uwierzytelnianie podstawowe), itp.).
### Przykład
// !! Assumes variable fileURL contains a valid URL to a path on the device,
// for example, cdvfile://localhost/persistent/path/to/downloads/
var fileTransfer = new FileTransfer();
var uri = encodeURI("http://some.server.com/download.php");
fileTransfer.download(
uri,
fileURL,
function(entry) {
console.log("download complete: " + entry.toURL());
},
function(error) {
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("upload error code" + error.code);
},
false,
{
headers: {
"Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
}
}
);
### WP8 dziwactwa
* Pobierz wnioski są buforowane przez rodzimych realizacji. Aby uniknąć, buforowanie, przekazać `if-Modified-Since` nagłówka do pobrania Metoda.
## abort
Przerywa w toku transferu. Onerror callback jest przekazywany obiekt FileTransferError, który kod błędu z FileTransferError.ABORT_ERR.
### Przykład
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function(r) {
console.log("Should not be called.");
}
var fail = function(error) {
// error.code == FileTransferError.ABORT_ERR
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName="myphoto.jpg";
options.mimeType="image/jpeg";
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
ft.abort();
## FileTransferError
Obiekt `FileTransferError` jest przekazywana do błąd wywołania zwrotnego, gdy wystąpi błąd.
### Właściwości
* **Kod**: jeden z kodów błędów wstępnie zdefiniowanych poniżej. (Liczba)
* **Źródło**: URL do źródła. (String)
* **cel**: adres URL do docelowego. (String)
* **HTTP_STATUS**: kod stanu HTTP. Ten atrybut jest dostępna tylko po otrzymaniu kodu odpowiedzi z połączenia HTTP. (Liczba)
* **body** Treść odpowiedzi. Ten atrybut jest dostępna tylko wtedy, gdy odpowiedź jest otrzymanych od połączenia HTTP. (String)
* **exception**: albo e.getMessage lub e.toString (String)
### Stałe
* 1 = `FileTransferError.FILE_NOT_FOUND_ERR`
* 2 = `FileTransferError.INVALID_URL_ERR`
* 3 = `FileTransferError.CONNECTION_ERR`
* 4 = `FileTransferError.ABORT_ERR`
* 5 = `FileTransferError.NOT_MODIFIED_ERR`
## Do tyłu zgodności stwierdza
Poprzednie wersje tego pluginu tylko zaakceptować urządzenia bezwzględnych ścieżek jako źródło dla przekazywania, lub w celu pobrania. Te ścieżki będzie zazwyczaj formy
/var/mobile/Applications/<application UUID>/Documents/path/to/file (iOS)
/storage/emulated/0/path/to/file (Android)
Do tyłu zgodności, akceptowane są jeszcze te ścieżki, i jeśli aplikacja nagrał ścieżki, jak te w trwałej pamięci, następnie można nadal stosować.
Te ścieżki były narażone wcześniej we właściwości `fullPath` `FileEntry` i `DirectoryEntry` obiektów zwróconych przez wtyczki pliku. Nowe wersje pliku plugin, jednak już wystawiać te ścieżki do JavaScript.
Jeśli uaktualniasz nowy (1.0.0 lub nowsza) wersja pliku i mieć wcześniej przy `entry.fullPath` jako argumenty `download()` lub `upload() służącą`, a następnie trzeba będzie zmienić kod aby używać adresów URL plików zamiast.
`FileEntry.toURL()` i `DirectoryEntry.toURL()` zwraca adres URL plików formularza
cdvfile://localhost/persistent/path/to/file
które mogą być używane zamiast bezwzględna ścieżka zarówno `download()` i `metody upload()` metody.

View File

@@ -0,0 +1,302 @@
<!---
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
# cordova-plugin-file-transfer
Plugin pozwala na przesyłanie i pobieranie plików.
Ten plugin określa globalne `FileTransfer`, `FileUploadOptions` konstruktorów.
Chociaż w globalnym zasięgu, są nie dostępne dopiero po `deviceready` imprezie.
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
console.log(FileTransfer);
}
## Instalacja
cordova plugin add cordova-plugin-file-transfer
## Obsługiwane platformy
* Amazon Fire OS
* Android
* BlackBerry 10
* Przeglądarka
* Firefox OS **
* iOS
* Windows Phone 7 i 8 *
* Windows 8
* Windows
* *Nie obsługują `onprogress` ani `abort()`*
* * *Nie obsługują `onprogress`*
# FileTransfer
Obiekt `FileTransfer` zapewnia sposób wgrać pliki przy użyciu żądania HTTP wieloczęściowe POST i pobierania plików, jak również.
## Właściwości
* **OnProgress**: o nazwie `ProgressEvent` gdy nowy kawałek danych jest przenoszona. *(Funkcja)*
## Metody
* **wgraj**: wysyła plik na serwer.
* **do pobrania**: pliki do pobrania pliku z serwera.
* **przerwać**: przerywa w toku transferu.
## upload
**Parametry**:
* **fileURL**: URL plików reprezentujących pliku na urządzenie. Dla wstecznej kompatybilności, to może również być pełną ścieżkę pliku na urządzenie. (Zobacz [wstecz zgodności zauważa] poniżej)
* **serwer**: adres URL serwera, aby otrzymać plik, jak kodowane przez`encodeURI()`.
* **successCallback**: wywołania zwrotnego, który jest przekazywany obiekt `FileUploadResult`. *(Funkcja)*
* **errorCallback**: wywołanie zwrotne, które wykonuje, jeżeli wystąpi błąd pobierania `FileUploadResult`. Wywoływany z obiektu `FileTransferError`. *(Funkcja)*
* **Opcje**: parametry opcjonalne *(obiektu)*. Ważne klucze:
* **fileKey**: nazwa elementu form. Domyślnie `file` . (DOMString)
* **Nazwa pliku**: nazwy pliku, aby użyć podczas zapisywania pliku na serwerze. Domyślnie `image.jpg` . (DOMString)
* **element httpMethod**: Metoda HTTP do użycia - `umieścić` lub `POST`. Domyślnie `POST`. (DOMString)
* **mimeType**: Typ mime danych do przesłania. Domyślnie do `image/jpeg`. (DOMString)
* **params**: zestaw par opcjonalny klucz/wartość w żądaniu HTTP. (Obiekt)
* **chunkedMode**: czy przekazać dane w trybie pakietowego przesyłania strumieniowego. Wartością domyślną jest `true`. (Wartość logiczna)
* **headers**: Mapa wartości Nazwa/nagłówka nagłówek. Aby określić więcej niż jedną wartość, należy użyć tablicę. (Obiekt)
* **trustAllHosts**: parametr opcjonalny, domyślnie `false` . Jeśli zestaw `true` , to akceptuje wszystkie certyfikaty bezpieczeństwa. Jest to przydatne, ponieważ Android odrzuca Certyfikaty samopodpisane. Nie zaleca się do użytku produkcyjnego. Obsługiwane na Androida i iOS. *(wartość logiczna)*
### Przykład
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function (r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
var fail = function (error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
options.mimeType = "text/plain";
var params = {};
params.value1 = "test";
params.value2 = "param";
options.params = params;
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
### Przykład z Prześlij nagłówki i zdarzeń postępu (Android i iOS tylko)
function win(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
function fail(error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var uri = encodeURI("http://some.server.com/upload.php");
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1);
options.mimeType="text/plain";
var headers={'headerParam':'headerValue'};
options.headers = headers;
var ft = new FileTransfer();
ft.onprogress = function(progressEvent) {
if (progressEvent.lengthComputable) {
loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
} else {
loadingStatus.increment();
}
};
ft.upload(fileURL, uri, win, fail, options);
## FileUploadResult
Obiekt `FileUploadResult` jest przekazywana do sukcesu wywołania zwrotnego metody `upload() służącą` obiektu `FileTransfer`.
### Właściwości
* **bytesSent**: liczba bajtów wysłanych do serwera jako część upload. (długie)
* **responseCode**: kod odpowiedzi HTTP, zwracane przez serwer. (długie)
* **odpowiedź**: HTTP odpowiedzi zwracane przez serwer. (DOMString)
* **nagłówki**: nagłówki HTTP odpowiedzi przez serwer. (Obiekt)
* Obecnie obsługiwane na iOS tylko.
### Dziwactwa iOS
* Nie obsługuje `responseCode` lub`bytesSent`.
## download
**Parametry**:
* **Źródło**: adres URL serwera, aby pobrać plik, jak kodowane przez`encodeURI()`.
* **cel**: url plików reprezentujących pliku na urządzenie. Dla wstecznej kompatybilności, to może również być pełną ścieżkę pliku na urządzenie. (Zobacz [wstecz zgodności zauważa] poniżej)
* **successCallback**: wywołania zwrotnego, który jest przekazywany `FileEntry` obiektu. *(Funkcja)*
* **errorCallback**: wywołanie zwrotne, które wykonuje, jeśli wystąpi błąd podczas pobierania `FileEntry`. Wywoływany z obiektu `FileTransferError`. *(Funkcja)*
* **trustAllHosts**: parametr opcjonalny, domyślnie `false` . Jeśli zestaw `true` , to akceptuje wszystkie certyfikaty bezpieczeństwa. Jest to przydatne, ponieważ Android odrzuca Certyfikaty samopodpisane. Nie zaleca się do użytku produkcyjnego. Obsługiwane na Androida i iOS. *(wartość logiczna)*
* **Opcje**: parametry opcjonalne, obecnie tylko obsługuje nagłówki (takie jak autoryzacja (uwierzytelnianie podstawowe), itp.).
### Przykład
// !! Assumes variable fileURL contains a valid URL to a path on the device,
// for example, cdvfile://localhost/persistent/path/to/downloads/
var fileTransfer = new FileTransfer();
var uri = encodeURI("http://some.server.com/download.php");
fileTransfer.download(
uri,
fileURL,
function(entry) {
console.log("download complete: " + entry.toURL());
},
function(error) {
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("upload error code" + error.code);
},
false,
{
headers: {
"Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
}
}
);
## abort
Przerywa w toku transferu. Onerror callback jest przekazywany obiekt FileTransferError, który kod błędu z FileTransferError.ABORT_ERR.
### Przykład
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function(r) {
console.log("Should not be called.");
}
var fail = function(error) {
// error.code == FileTransferError.ABORT_ERR
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName="myphoto.jpg";
options.mimeType="image/jpeg";
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
ft.abort();
## FileTransferError
Obiekt `FileTransferError` jest przekazywana do błąd wywołania zwrotnego, gdy wystąpi błąd.
### Właściwości
* **Kod**: jeden z kodów błędów wstępnie zdefiniowanych poniżej. (Liczba)
* **Źródło**: URL do źródła. (String)
* **cel**: adres URL do docelowego. (String)
* **HTTP_STATUS**: kod stanu HTTP. Ten atrybut jest dostępna tylko po otrzymaniu kodu odpowiedzi z połączenia HTTP. (Liczba)
* **body** Treść odpowiedzi. Ten atrybut jest dostępna tylko wtedy, gdy odpowiedź jest otrzymanych od połączenia HTTP. (String)
* **exception**: albo e.getMessage lub e.toString (String)
### Stałe
* 1 = `FileTransferError.FILE_NOT_FOUND_ERR`
* 2 = `FileTransferError.INVALID_URL_ERR`
* 3 = `FileTransferError.CONNECTION_ERR`
* 4 = `FileTransferError.ABORT_ERR`
* 5 = `FileTransferError.NOT_MODIFIED_ERR`
## Do tyłu zgodności stwierdza
Poprzednie wersje tego pluginu tylko zaakceptować urządzenia bezwzględnych ścieżek jako źródło dla przekazywania, lub w celu pobrania. Te ścieżki będzie zazwyczaj formy
/var/mobile/Applications/<application UUID>/Documents/path/to/file (iOS)
/storage/emulated/0/path/to/file (Android)
Do tyłu zgodności, akceptowane są jeszcze te ścieżki, i jeśli aplikacja nagrał ścieżki, jak te w trwałej pamięci, następnie można nadal stosować.
Te ścieżki były narażone wcześniej we właściwości `fullPath` `FileEntry` i `DirectoryEntry` obiektów zwróconych przez wtyczki pliku. Nowe wersje pliku plugin, jednak już wystawiać te ścieżki do JavaScript.
Jeśli uaktualniasz nowy (1.0.0 lub nowsza) wersja pliku i mieć wcześniej przy `entry.fullPath` jako argumenty `download()` lub `upload() służącą`, a następnie trzeba będzie zmienić kod aby używać adresów URL plików zamiast.
`FileEntry.toURL()` i `DirectoryEntry.toURL()` zwraca adres URL plików formularza
cdvfile://localhost/persistent/path/to/file
które mogą być używane zamiast bezwzględna ścieżka zarówno `download()` i `metody upload()` metody.

View File

@@ -0,0 +1,290 @@
<!---
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
# cordova-plugin-file-transfer
Этот плагин позволяет вам загружать и скачивать файлы.
## Установка
cordova plugin add cordova-plugin-file-transfer
## Поддерживаемые платформы
* Amazon Fire OS
* Android
* BlackBerry 10
* Firefox OS **
* iOS
* Windows Phone 7 и 8 *
* Windows 8 ***|
* Windows ***|
* *Не поддерживают `onprogress` , ни `abort()` *
** *Не поддерживает `onprogress` *
Частичная поддержка `onprogress` для закачки метод. `onprogress` вызывается с пустой ход событий благодаря Windows limitations_
# FileTransfer
`FileTransfer`Объект предоставляет способ для загрузки файлов с помощью нескольких частей запроса POST HTTP и для загрузки файлов, а также.
## Параметры
* **OnProgress**: называется с `ProgressEvent` всякий раз, когда новый фрагмент данных передается. *(Функция)*
## Методы
* **добавлено**: отправляет файл на сервер.
* **скачать**: Скачать файл с сервера.
* **прервать**: прерывает передачу в прогресс.
## загрузить
**Параметры**:
* **fileURL**: файловой системы URL-адрес, представляющий файл на устройстве. Для обратной совместимости, это также может быть полный путь к файлу на устройстве. (См. [обратной совместимости отмечает] ниже)
* **сервер**: URL-адрес сервера, чтобы получить файл, как закодированные`encodeURI()`.
* **successCallback**: обратного вызова, передаваемого `Metadata` объект. *(Функция)*
* **errorCallback**: обратного вызова, который выполняется в случае получения ошибки `Metadata` . Вызываемый с `FileTransferError` объект. *(Функция)*
* **опции**: необязательные параметры *(объект)*. Допустимые ключи:
* **fileKey**: имя элемента form. По умолчанию `file` . (DOMString)
* **имя файла**: имя файла для использования при сохранении файла на сервере. По умолчанию `image.jpg` . (DOMString)
* **mimeType**: mime-тип данных для загрузки. По умолчанию `image/jpeg` . (DOMString)
* **params**: набор пар дополнительный ключ/значение для передачи в HTTP-запросе. (Объект)
* **chunkedMode**: следует ли загружать данные в фрагментарности потоковом режиме. По умолчанию `true` . (Логическое значение)
* **заголовки**: Карта значений заголовок имя заголовка. Используйте массив для указания более одного значения. (Объект)
* **trustAllHosts**: необязательный параметр, по умолчанию `false` . Если значение `true` , она принимает все сертификаты безопасности. Это полезно, поскольку Android отвергает самозаверяющие сертификаты. Не рекомендуется для использования в производстве. Поддерживается на Android и iOS. *(логическое значение)*
### Пример
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function (r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
var fail = function (error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
options.mimeType = "text/plain";
var params = {};
params.value1 = "test";
params.value2 = "param";
options.params = params;
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
### Пример с загружать заголовки и события Progress (Android и iOS только)
function win(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
function fail(error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var uri = encodeURI("http://some.server.com/upload.php");
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1);
options.mimeType="text/plain";
var headers={'headerParam':'headerValue'};
options.headers = headers;
var ft = new FileTransfer();
ft.onprogress = function(progressEvent) {
if (progressEvent.lengthComputable) {
loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
} else {
loadingStatus.increment();
}
};
ft.upload(fileURL, uri, win, fail, options);
## FileUploadResult
Объект `FileUploadResult` передается на успех обратного вызова метода `upload()` объекта `FileTransfer`.
### Параметры
* **bytesSent**: количество байт, отправленных на сервер как часть загрузки. (длинная)
* **responseCode**: код ответа HTTP, возвращаемых сервером. (длинная)
* **ответ**: ответ HTTP, возвращаемых сервером. (DOMString)
* **заголовки**: заголовки ответов HTTP-сервером. (Объект)
* В настоящее время поддерживается только для iOS.
### Особенности iOS
* Не поддерживает `responseCode` или`bytesSent`.
## Скачать
**Параметры**:
* **источник**: URL-адрес сервера для загрузки файла, как закодированные`encodeURI()`.
* **Цель**: файловой системы URL-адрес, представляющий файл на устройстве. Для обратной совместимости, это также может быть полный путь к файлу на устройстве. (См. [обратной совместимости отмечает] ниже)
* **successCallback**: обратного вызова, передаваемого `FileEntry` объект. *(Функция)*
* **errorCallback**: обратного вызова, который выполняется, если возникает ошибка при получении `Metadata` . Вызываемый с `FileTransferError` объект. *(Функция)*
* **trustAllHosts**: необязательный параметр, по умолчанию `false` . Если значение `true` , она принимает все сертификаты безопасности. Это полезно, потому что Android отвергает самозаверяющие сертификаты. Не рекомендуется для использования в производстве. Поддерживается на Android и iOS. *(логическое значение)*
* **опции**: необязательные параметры, в настоящее время только поддерживает заголовки (например авторизации (базовая аутентификация) и т.д.).
### Пример
// !! Assumes variable fileURL contains a valid URL to a path on the device,
// for example, cdvfile://localhost/persistent/path/to/downloads/
var fileTransfer = new FileTransfer();
var uri = encodeURI("http://some.server.com/download.php");
fileTransfer.download(
uri,
fileURL,
function(entry) {
console.log("download complete: " + entry.toURL());
},
function(error) {
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("upload error code" + error.code);
},
false,
{
headers: {
"Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
}
}
);
## прервать
Прерывает передачу в прогресс. Onerror обратного вызова передается объект FileTransferError, который имеет код ошибки FileTransferError.ABORT_ERR.
### Пример
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function(r) {
console.log("Should not be called.");
}
var fail = function(error) {
// error.code == FileTransferError.ABORT_ERR
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName="myphoto.jpg";
options.mimeType="image/jpeg";
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
ft.abort();
## FileTransferError
A `FileTransferError` при ошибке обратного вызова передается объект, при возникновении ошибки.
### Параметры
* **код**: один из кодов стандартных ошибок, перечисленные ниже. (Число)
* **источник**: URL-адрес источника. (Строка)
* **Цель**: URL-адрес к целевому объекту. (Строка)
* **http_status**: код состояния HTTP. Этот атрибут доступен только при код ответа от HTTP-соединения. (Число)
* **исключение**: либо e.getMessage или e.toString (строка)
### Константы
* 1 = `FileTransferError.FILE_NOT_FOUND_ERR`
* 2 = `FileTransferError.INVALID_URL_ERR`
* 3 = `FileTransferError.CONNECTION_ERR`
* 4 = `FileTransferError.ABORT_ERR`
* 5 = `FileTransferError.NOT_MODIFIED_ERR`
## Обратной совместимости отмечает
Предыдущие версии этого плагина будет принимать только устройства Абсолют файлам как источник для закачки, или как целевых для загрузок. Обычно эти пути бы формы
/var/mobile/Applications/<application UUID>/Documents/path/to/file (iOS)
/storage/emulated/0/path/to/file (Android)
Для обратной совместимости, по-прежнему принимаются эти пути, и если ваше приложение зарегистрировано пути как в постоянное хранилище, то они могут продолжать использоваться.
Эти пути ранее были видны в `fullPath` свойства `FileEntry` и `DirectoryEntry` объекты, возвращаемые файл плагина. Новые версии файла плагина, однако, не подвергать эти пути в JavaScript.
Если вы переходите на новый (1.0.0 или новее) версию файла и вы ранее использовали `entry.fullPath` в качестве аргументов `download()` или `upload()` , то вам необходимо будет изменить код для использования файловой системы URL вместо.
`FileEntry.toURL()`и `DirectoryEntry.toURL()` возвращает URL-адрес формы файловой системы
cdvfile://localhost/persistent/path/to/file
которые могут быть использованы вместо абсолютного пути в обоих `download()` и `upload()` методы.

View File

@@ -0,0 +1,311 @@
<!--
# license: Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-->
# cordova-plugin-file-transfer
[![Build Status](https://travis-ci.org/apache/cordova-plugin-file-transfer.svg)](https://travis-ci.org/apache/cordova-plugin-file-transfer)
外掛程式檔: <doc/index.md>
這個外掛程式允許你上傳和下載檔案。
這個外掛程式定義全域 `FileTransfer``FileUploadOptions` 的建構函式。
雖然在全球範圍內,他們不可用直到 `deviceready` 事件之後。
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
console.log(FileTransfer);
}
## 安裝
cordova plugin add cordova-plugin-file-transfer
## 支援的平臺
* 亞馬遜火 OS
* Android 系統
* 黑莓 10
* 瀏覽器
* 火狐瀏覽器的作業系統 * *
* iOS
* Windows Phone 7 和 8 *
* Windows 8
* Windows
\ **不支援`onprogress``abort()` *
\ * **不支援`onprogress` *
# 檔案傳輸
`FileTransfer`物件提供上傳檔使用 HTTP 多部分職位或付諸表決的請求,並將檔以及下載的方式。
## 屬性
* **onprogress** 使用調用 `ProgressEvent` 每當一塊新的資料傳輸。*(函數)*
## 方法
* **upload** 將檔發送到伺服器。
* **download** 從伺服器上下載檔案。
* **abort**: 中止正在進行轉讓。
## upload
**參數**
* **fileURL** 表示檔在設備上的檔案系統 URL。 為向後相容性,這也可以將設備上的檔的完整路徑。 (請參見 [向後相容性注意到] 下面)
* **server** 伺服器以接收該檔,由編碼的 URL`encodeURI()`.
* **successCallback** 一個通過一個 `FileUploadResult` 物件的回檔。*(函數)*
* **errorCallback** 如果發生錯誤,檢索 `FileUploadResult` 執行一個回檔。使用 `FileTransferError` 物件調用。*(函數)*
* **options** 可選參數*(物件)*。有效的金鑰:
* **fileKey** 表單元素的名稱。預設值為 `file` 。() DOMString
* **fileName** 要保存在伺服器上的檔時使用的檔案名稱。預設值為 `image.jpg` 。() DOMString
* **httpMethod** HTTP 方法使用-`PUT``POST`。預設值為 `POST`。() DOMString
* **mimeType** 要上載的資料的 mime 類型。預設設置為 `image/jpeg`。() DOMString
* **params** 一組要在 HTTP 要求中傳遞的可選的鍵值對。(物件)
* **chunkedMode** 是否要分塊的流式處理模式中的資料上載。預設值為 `true`。(布林值)
* **headers**: 地圖的標頭名稱/標頭值。 使用陣列來指定多個值。 IOS、 FireOS和安卓系統如果已命名的內容類型標頭存在多部分表單資料不被使用。 (Object)
* **httpMethod**: HTTP 方法,例如使用張貼或放。 預設為`開機自檢`。() DOMString
* **trustAllHosts**: 可選參數,預設值為 `false` 。 如果設置為 `true` ,它可以接受的所有安全證書。 這是有用的因為 android 系統拒絕自簽名的安全證書。 不建議供生產使用。 在 Android 和 iOS 上受支援。 *(布林值)*
### 示例
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function (r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
var fail = function (error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
options.mimeType = "text/plain";
var params = {};
params.value1 = "test";
params.value2 = "param";
options.params = params;
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
### 與上傳的標頭和進度事件 Android 和 iOS 只) 的示例
function win(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
function fail(error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var uri = encodeURI("http://some.server.com/upload.php");
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1);
options.mimeType="text/plain";
var headers={'headerParam':'headerValue'};
options.headers = headers;
var ft = new FileTransfer();
ft.onprogress = function(progressEvent) {
if (progressEvent.lengthComputable) {
loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
} else {
loadingStatus.increment();
}
};
ft.upload(fileURL, uri, win, fail, options);
## FileUploadResult
`FileUploadResult` 物件將傳遞給該 `檔案傳輸` 物件的 `upload()` 方法的成功回檔。
### 屬性
* **bytesSent** 作為上載的一部分發送到伺服器的位元組數。(長)
* **responseCode** 由伺服器返回的 HTTP 回應代碼。(長)
* **response** 由伺服器返回的 HTTP 回應。() DOMString
* **headers** 由伺服器的 HTTP 回應標頭。(物件)
* 目前支援的 iOS 只。
### iOS 的怪癖
* 不支援 `responseCode``bytesSent`.
## download
**參數**
* **source** 要下載的檔,如由編碼的伺服器的 URL`encodeURI()`.
* **target** 表示檔在設備上的檔案系統 url。 為向後相容性,這也可以將設備上的檔的完整路徑。 (請參見 [向後相容性注意到] 下面)
* **successCallback** 傳遞一個回檔 `FileEntry` 物件。*(函數)*
* **errorCallback** 如果檢索 `FileEntry` 時發生錯誤,則執行一個回檔。使用 `FileTransferError` 物件調用。*(函數)*
* **trustAllHosts**: 可選參數,預設值為 `false` 。 如果設置為 `true` ,它可以接受的所有安全證書。 這是有用的因為 Android 拒絕自行簽署式安全證書。 不建議供生產使用。 在 Android 和 iOS 上受支援。 *(布林值)*
* **options** 可選參數,目前只支援標題 (如授權 (基本驗證) 等)。
### 示例
// !! Assumes variable fileURL contains a valid URL to a path on the device,
// for example, cdvfile://localhost/persistent/path/to/downloads/
var fileTransfer = new FileTransfer();
var uri = encodeURI("http://some.server.com/download.php");
fileTransfer.download(
uri,
fileURL,
function(entry) {
console.log("download complete: " + entry.toURL());
},
function(error) {
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("upload error code" + error.code);
},
false,
{
headers: {
"Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
}
}
);
### WP8 的怪癖
* 下載請求由本機實現被緩存。若要避免緩存,傳遞`如果修改自`郵件頭以下載方法。
## abort
中止正在進行轉讓。Onerror 回檔傳遞一個 FileTransferError 物件具有 FileTransferError.ABORT_ERR 錯誤代碼。
### 示例
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function(r) {
console.log("Should not be called.");
}
var fail = function(error) {
// error.code == FileTransferError.ABORT_ERR
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName="myphoto.jpg";
options.mimeType="image/jpeg";
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
ft.abort();
## FileTransferError
當發生錯誤時,`FileTransferError` 物件將傳遞給錯誤回檔。
### 屬性
* **code** 下面列出的預定義的錯誤代碼之一。(人數)
* **source** 源的 URL。字串
* **target** 到目標 URL。字串
* **HTTP_status** HTTP 狀態碼。從 HTTP 連接收到一個回應代碼時,此屬性才可用。(人數)
* **body**回應正文。此屬性只能是可用的當該 HTTP 連接收到答覆。(字串)
* **exception** 要麼 e.getMessage 或 e.toString (字串)
### 常量
* 1 = `FileTransferError.FILE_NOT_FOUND_ERR`
* 2 = `FileTransferError.INVALID_URL_ERR`
* 3 = `FileTransferError.CONNECTION_ERR`
* 4 = `FileTransferError.ABORT_ERR`
* 5 = `FileTransferError.NOT_MODIFIED_ERR`
## 向後相容性注意到
以前版本的這個外掛程式才會接受設備-絕對檔路徑作為源對於上載,或用於下載的目標。這些路徑通常會在表單
/var/mobile/Applications/<application UUID>/Documents/path/to/file (iOS)
/storage/emulated/0/path/to/file (Android)
為向後相容性,這些路徑仍會被接受,和如果您的應用程式已錄得像這些在持久性存儲的路徑,然後他們可以繼續使用。
這些路徑被以前暴露在 `FileEntry` 和由檔外掛程式返回的 `DirectoryEntry` 物件的 `fullPath` 屬性中。 新版本的檔的外掛程式,但是,不再公開這些 JavaScript 的路徑。
如果您要升級到新 (1.0.0 或更高版本) 版本的檔,和你以前一直在使用 `entry.fullPath` 作為參數到 `download()``upload()`,那麼你將需要更改代碼以使用檔案系統的 Url 來代替。
`FileEntry.toURL()``DirectoryEntry.toURL()` 返回的表單檔案 URL
cdvfile://localhost/persistent/path/to/file
它可以用在 `download()``upload()` 兩種方法中的絕對檔路徑位置。

View File

@@ -0,0 +1,302 @@
<!---
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
# cordova-plugin-file-transfer
這個外掛程式允許你上傳和下載檔案。
這個外掛程式定義全域 `FileTransfer``FileUploadOptions` 的建構函式。
雖然在全球範圍內,他們不可用直到 `deviceready` 事件之後。
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
console.log(FileTransfer);
}
## 安裝
cordova plugin add cordova-plugin-file-transfer
## 支援的平臺
* 亞馬遜火 OS
* Android 系統
* 黑莓 10
* 瀏覽器
* 火狐瀏覽器的作業系統 * *
* iOS
* Windows Phone 7 和 8 *
* Windows 8
* Windows
* *不支援 `onprogress``abort()`*
* * *不支援 `onprogress`*
# FileTransfer
`FileTransfer` 物件提供一種使用 HTTP 多部分 POST 請求的檔上傳,下載檔案以及方式。
## 屬性
* **onprogress** 使用調用 `ProgressEvent` 每當一塊新的資料傳輸。*(函數)*
## 方法
* **upload** 將檔發送到伺服器。
* **download** 從伺服器上下載檔案。
* **abort**: 中止正在進行轉讓。
## upload
**參數**
* **fileURL** 表示檔在設備上的檔案系統 URL。 為向後相容性,這也可以將設備上的檔的完整路徑。 (請參見 [向後相容性注意到] 下面)
* **server** 伺服器以接收該檔,由編碼的 URL`encodeURI()`.
* **successCallback** 一個通過一個 `FileUploadResult` 物件的回檔。*(函數)*
* **errorCallback** 如果發生錯誤,檢索 `FileUploadResult` 執行一個回檔。使用 `FileTransferError` 物件調用。*(函數)*
* **options** 可選參數*(物件)*。有效的金鑰:
* **fileKey** 表單元素的名稱。預設值為 `file` 。() DOMString
* **fileName** 要保存在伺服器上的檔時使用的檔案名稱。預設值為 `image.jpg` 。() DOMString
* **httpMethod** HTTP 方法使用-`PUT``POST`。預設值為 `POST`。() DOMString
* **mimeType** 要上載的資料的 mime 類型。預設設置為 `image/jpeg`。() DOMString
* **params** 一組要在 HTTP 要求中傳遞的可選的鍵值對。(物件)
* **chunkedMode** 是否要分塊的流式處理模式中的資料上載。預設值為 `true`。(布林值)
* **headers** 地圖的標頭名稱/標頭值。使用陣列來指定多個值。(物件)
* **trustAllHosts**: 可選參數,預設值為 `false` 。 如果設置為 `true` ,它接受的所有安全證書。 這是有用的因為 android 系統拒絕自簽名的安全證書。 不建議供生產使用。 支援 Android 和 iOS。 *(布林值)*
### 示例
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function (r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
var fail = function (error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
options.mimeType = "text/plain";
var params = {};
params.value1 = "test";
params.value2 = "param";
options.params = params;
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
### 與上傳的標頭和進度事件 Android 和 iOS 只) 的示例
function win(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
}
function fail(error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var uri = encodeURI("http://some.server.com/upload.php");
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName=fileURL.substr(fileURL.lastIndexOf('/')+1);
options.mimeType="text/plain";
var headers={'headerParam':'headerValue'};
options.headers = headers;
var ft = new FileTransfer();
ft.onprogress = function(progressEvent) {
if (progressEvent.lengthComputable) {
loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
} else {
loadingStatus.increment();
}
};
ft.upload(fileURL, uri, win, fail, options);
## FileUploadResult
`FileUploadResult` 物件將傳遞給該 `檔案傳輸` 物件的 `upload()` 方法的成功回檔。
### 屬性
* **bytesSent** 作為上載的一部分發送到伺服器的位元組數。(長)
* **responseCode** 由伺服器返回的 HTTP 回應代碼。(長)
* **response** 由伺服器返回的 HTTP 回應。() DOMString
* **headers** 由伺服器的 HTTP 回應標頭。(物件)
* 目前支援的 iOS 只。
### iOS 的怪癖
* 不支援 `responseCode``bytesSent`.
## download
**參數**
* **source** 要下載的檔,如由編碼的伺服器的 URL`encodeURI()`.
* **target** 表示檔在設備上的檔案系統 url。 為向後相容性,這也可以將設備上的檔的完整路徑。 (請參見 [向後相容性注意到] 下面)
* **successCallback** 傳遞一個回檔 `FileEntry` 物件。*(函數)*
* **errorCallback** 如果檢索 `FileEntry` 時發生錯誤,則執行一個回檔。使用 `FileTransferError` 物件調用。*(函數)*
* **trustAllHosts**: 可選參數,預設值為 `false` 。 如果設置為 `true` ,它可以接受的所有安全證書。 這是有用的因為 Android 拒絕自行簽署式安全證書。 不建議供生產使用。 在 Android 和 iOS 上受支援。 *(布林值)*
* **options** 可選參數,目前只支援標題 (如授權 (基本驗證) 等)。
### 示例
// !! Assumes variable fileURL contains a valid URL to a path on the device,
// for example, cdvfile://localhost/persistent/path/to/downloads/
var fileTransfer = new FileTransfer();
var uri = encodeURI("http://some.server.com/download.php");
fileTransfer.download(
uri,
fileURL,
function(entry) {
console.log("download complete: " + entry.toURL());
},
function(error) {
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("upload error code" + error.code);
},
false,
{
headers: {
"Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
}
}
);
## abort
中止正在進行轉讓。Onerror 回檔傳遞一個 FileTransferError 物件具有 FileTransferError.ABORT_ERR 錯誤代碼。
### 示例
// !! Assumes variable fileURL contains a valid URL to a text file on the device,
// for example, cdvfile://localhost/persistent/path/to/file.txt
var win = function(r) {
console.log("Should not be called.");
}
var fail = function(error) {
// error.code == FileTransferError.ABORT_ERR
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName="myphoto.jpg";
options.mimeType="image/jpeg";
var ft = new FileTransfer();
ft.upload(fileURL, encodeURI("http://some.server.com/upload.php"), win, fail, options);
ft.abort();
## FileTransferError
當發生錯誤時,`FileTransferError` 物件將傳遞給錯誤回檔。
### 屬性
* **code** 下面列出的預定義的錯誤代碼之一。(人數)
* **source** 源的 URL。字串
* **target** 到目標 URL。字串
* **HTTP_status** HTTP 狀態碼。從 HTTP 連接收到一個回應代碼時,此屬性才可用。(人數)
* **body**回應正文。此屬性只能是可用的當該 HTTP 連接收到答覆。(字串)
* **exception** 要麼 e.getMessage 或 e.toString (字串)
### 常量
* 1 = `FileTransferError.FILE_NOT_FOUND_ERR`
* 2 = `FileTransferError.INVALID_URL_ERR`
* 3 = `FileTransferError.CONNECTION_ERR`
* 4 = `FileTransferError.ABORT_ERR`
* 5 = `FileTransferError.NOT_MODIFIED_ERR`
## 向後相容性注意到
以前版本的這個外掛程式才會接受設備-絕對檔路徑作為源對於上載,或用於下載的目標。這些路徑通常會在表單
/var/mobile/Applications/<application UUID>/Documents/path/to/file (iOS)
/storage/emulated/0/path/to/file (Android)
為向後相容性,這些路徑仍會被接受,和如果您的應用程式已錄得像這些在持久性存儲的路徑,然後他們可以繼續使用。
這些路徑被以前暴露在 `FileEntry` 和由檔外掛程式返回的 `DirectoryEntry` 物件的 `fullPath` 屬性中。 新版本的檔的外掛程式,但是,不再公開這些 JavaScript 的路徑。
如果您要升級到新 (1.0.0 或更高版本) 版本的檔,和你以前一直在使用 `entry.fullPath` 作為參數到 `download()``upload()`,那麼你將需要更改代碼以使用檔案系統的 Url 來代替。
`FileEntry.toURL()``DirectoryEntry.toURL()` 返回的表單檔案 URL
cdvfile://localhost/persistent/path/to/file
它可以用在 `download()``upload()` 兩種方法中的絕對檔路徑位置。

View File

@@ -0,0 +1,64 @@
{
"name": "cordova-plugin-file-transfer",
"version": "1.7.1",
"description": "Cordova File Transfer Plugin",
"types": "./types/index.d.ts",
"cordova": {
"id": "cordova-plugin-file-transfer",
"platforms": [
"android",
"amazon-fireos",
"ubuntu",
"blackberry10",
"ios",
"wp7",
"wp8",
"windows8",
"windows",
"firefoxos",
"browser"
]
},
"scripts": {
"test": "npm run lint && npm run style",
"lint": "jshint www && jshint src && jshint tests",
"style": "jscs tests/tests.js"
},
"repository": {
"type": "git",
"url": "https://github.com/apache/cordova-plugin-file-transfer"
},
"bugs": {
"url": "https://issues.apache.org/jira/browse/CB"
},
"keywords": [
"cordova",
"file",
"transfer",
"ecosystem:cordova",
"cordova-android",
"cordova-amazon-fireos",
"cordova-ubuntu",
"cordova-blackberry10",
"cordova-ios",
"cordova-wp7",
"cordova-wp8",
"cordova-windows8",
"cordova-windows",
"cordova-firefoxos",
"cordova-browser"
],
"author": "Apache Software Foundation",
"license": "Apache-2.0",
"engines": {
"cordovaDependencies": {
"2.0.0": {
"cordova": ">100"
}
}
},
"devDependencies": {
"jscs": "^2.6.0",
"jshint": "^2.8.0"
}
}

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="cordova-plugin-file-transfer"
version="1.7.1">
<name>File Transfer</name>
<description>Cordova File Transfer Plugin</description>
<license>Apache 2.0</license>
<keywords>cordova,file,transfer</keywords>
<repo>https://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer.git</repo>
<issue>https://issues.apache.org/jira/browse/CB/component/12320650</issue>
<dependency id="cordova-plugin-file" version=">=5.0.0" />
<js-module src="www/FileTransferError.js" name="FileTransferError">
<clobbers target="window.FileTransferError" />
</js-module>
<js-module src="www/FileTransfer.js" name="FileTransfer">
<clobbers target="window.FileTransfer" />
</js-module>
<!-- android -->
<platform name="android">
<config-file target="res/xml/config.xml" parent="/*">
<feature name="FileTransfer" >
<param name="android-package" value="org.apache.cordova.filetransfer.FileTransfer"/>
</feature>
</config-file>
<config-file target="AndroidManifest.xml" parent="/*">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</config-file>
<source-file src="src/android/FileTransfer.java" target-dir="src/org/apache/cordova/filetransfer" />
<source-file src="src/android/FileProgressResult.java" target-dir="src/org/apache/cordova/filetransfer" />
<source-file src="src/android/FileUploadResult.java" target-dir="src/org/apache/cordova/filetransfer" />
</platform>
<!-- amamzon-fireos -->
<platform name="amazon-fireos">
<config-file target="res/xml/config.xml" parent="/*">
<feature name="FileTransfer" >
<param name="android-package" value="org.apache.cordova.filetransfer.FileTransfer"/>
</feature>
</config-file>
<config-file target="AndroidManifest.xml" parent="/*">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</config-file>
<source-file src="src/amazon/FileTransfer.java" target-dir="src/org/apache/cordova/filetransfer" />
<source-file src="src/android/FileProgressResult.java" target-dir="src/org/apache/cordova/filetransfer" />
<source-file src="src/android/FileUploadResult.java" target-dir="src/org/apache/cordova/filetransfer" />
</platform>
<!-- ubuntu -->
<platform name="ubuntu">
<header-file src="src/ubuntu/file-transfer.h" />
<source-file src="src/ubuntu/file-transfer.cpp" />
</platform>
<!-- blackberry10 -->
<platform name="blackberry10">
<config-file target="www/config.xml" parent="/widget">
<feature name="FileTransfer" value="FileTransfer"></feature>
</config-file>
<js-module src="www/blackberry10/FileTransferProxy.js" name="FileTransferProxy" >
<runs />
</js-module>
<js-module src="www/blackberry10/xhrFileTransfer.js" name="xhrFileTransfer" />
<!--
<js-module src="www/blackberry10/abort.js" name="abortProxy" />
<js-module src="www/blackberry10/download.js" name="downloadProxy" />
<js-module src="www/blackberry10/upload.js" name="uploadProxy" />
-->
</platform>
<!-- ios -->
<platform name="ios">
<config-file target="config.xml" parent="/*">
<feature name="FileTransfer">
<param name="ios-package" value="CDVFileTransfer" />
</feature>
</config-file>
<header-file src="src/ios/CDVFileTransfer.h" />
<source-file src="src/ios/CDVFileTransfer.m" />
<framework src="AssetsLibrary.framework" />
</platform>
<!-- wp7 -->
<platform name="wp7">
<config-file target="config.xml" parent="/*">
<feature name="FileTransfer">
<param name="wp-package" value="FileTransfer"/>
</feature>
</config-file>
<source-file src="src/wp/FileTransfer.cs" />
<js-module src="www/wp7/base64.js" name="base64">
<clobbers target="window.FileTransferBase64" />
</js-module>
</platform>
<!-- wp8 -->
<platform name="wp8">
<config-file target="config.xml" parent="/*">
<feature name="FileTransfer">
<param name="wp-package" value="FileTransfer"/>
</feature>
</config-file>
<source-file src="src/wp/FileTransfer.cs" />
</platform>
<!-- windows8 -->
<platform name="windows8">
<js-module src="src/windows/FileTransferProxy.js" name="FileTransferProxy">
<runs />
</js-module>
</platform>
<!-- windows -->
<platform name="windows">
<js-module src="src/windows/FileTransferProxy.js" name="FileTransferProxy">
<runs />
</js-module>
</platform>
<!-- firefoxOS -->
<platform name="firefoxos">
<config-file target="config.xml" parent="/*">
<permission name="systemXHR" privileged="true"/>
</config-file>
<js-module src="www/firefoxos/FileTransferProxy.js" name="FileTransferProxy">
<runs/>
</js-module>
</platform>
<!-- browser -->
<platform name="browser">
<js-module src="www/browser/FileTransfer.js" name="BrowserFileTransfer">
<clobbers target="window.FileTransfer" />
</js-module>
</platform>
</plugin>

View File

@@ -0,0 +1,898 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova.filetransfer;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Iterator;
import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.cordova.Config;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaResourceApi;
import org.apache.cordova.CordovaResourceApi.OpenForReadResult;
import org.apache.cordova.PluginResult;
import org.apache.cordova.file.FileUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.net.Uri;
import android.os.Build;
import android.util.Log;
import com.amazon.android.webkit.AmazonCookieManager;
public class FileTransfer extends CordovaPlugin {
private static final String LOG_TAG = "FileTransfer";
private static final String LINE_START = "--";
private static final String LINE_END = "\r\n";
private static final String BOUNDARY = "+++++";
public static int FILE_NOT_FOUND_ERR = 1;
public static int INVALID_URL_ERR = 2;
public static int CONNECTION_ERR = 3;
public static int ABORTED_ERR = 4;
private static HashMap<String, RequestContext> activeRequests = new HashMap<String, RequestContext>();
private static final int MAX_BUFFER_SIZE = 16 * 1024;
private static final class RequestContext {
String source;
String target;
File targetFile;
CallbackContext callbackContext;
InputStream currentInputStream;
OutputStream currentOutputStream;
boolean aborted;
RequestContext(String source, String target, CallbackContext callbackContext) {
this.source = source;
this.target = target;
this.callbackContext = callbackContext;
}
void sendPluginResult(PluginResult pluginResult) {
synchronized (this) {
if (!aborted) {
callbackContext.sendPluginResult(pluginResult);
}
}
}
}
/**
* Adds an interface method to an InputStream to return the number of bytes
* read from the raw stream. This is used to track total progress against
* the HTTP Content-Length header value from the server.
*/
private static abstract class TrackingInputStream extends FilterInputStream {
public TrackingInputStream(final InputStream in) {
super(in);
}
public abstract long getTotalRawBytesRead();
}
private static class ExposedGZIPInputStream extends GZIPInputStream {
public ExposedGZIPInputStream(final InputStream in) throws IOException {
super(in);
}
public Inflater getInflater() {
return inf;
}
}
/**
* Provides raw bytes-read tracking for a GZIP input stream. Reports the
* total number of compressed bytes read from the input, rather than the
* number of uncompressed bytes.
*/
private static class TrackingGZIPInputStream extends TrackingInputStream {
private ExposedGZIPInputStream gzin;
public TrackingGZIPInputStream(final ExposedGZIPInputStream gzin) throws IOException {
super(gzin);
this.gzin = gzin;
}
public long getTotalRawBytesRead() {
return gzin.getInflater().getBytesRead();
}
}
/**
* Provides simple total-bytes-read tracking for an existing InputStream
*/
private static class SimpleTrackingInputStream extends TrackingInputStream {
private long bytesRead = 0;
public SimpleTrackingInputStream(InputStream stream) {
super(stream);
}
private int updateBytesRead(int newBytesRead) {
if (newBytesRead != -1) {
bytesRead += newBytesRead;
}
return newBytesRead;
}
@Override
public int read() throws IOException {
return updateBytesRead(super.read());
}
// Note: FilterInputStream delegates read(byte[] bytes) to the below method,
// so we don't override it or else double count (CB-5631).
@Override
public int read(byte[] bytes, int offset, int count) throws IOException {
return updateBytesRead(super.read(bytes, offset, count));
}
public long getTotalRawBytesRead() {
return bytesRead;
}
}
@Override
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
if (action.equals("upload") || action.equals("download")) {
String source = args.getString(0);
String target = args.getString(1);
if (action.equals("upload")) {
try {
source = URLDecoder.decode(source, "UTF-8");
upload(source, target, args, callbackContext);
} catch (UnsupportedEncodingException e) {
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.MALFORMED_URL_EXCEPTION, "UTF-8 error."));
}
} else {
download(source, target, args, callbackContext);
}
return true;
} else if (action.equals("abort")) {
String objectId = args.getString(0);
abort(objectId);
callbackContext.success();
return true;
}
return false;
}
private static void addHeadersToRequest(URLConnection connection, JSONObject headers) {
try {
for (Iterator<?> iter = headers.keys(); iter.hasNext(); ) {
String headerKey = iter.next().toString();
JSONArray headerValues = headers.optJSONArray(headerKey);
if (headerValues == null) {
headerValues = new JSONArray();
headerValues.put(headers.getString(headerKey));
}
connection.setRequestProperty(headerKey, headerValues.getString(0));
for (int i = 1; i < headerValues.length(); ++i) {
connection.addRequestProperty(headerKey, headerValues.getString(i));
}
}
} catch (JSONException e1) {
// No headers to be manipulated!
}
}
/**
* Uploads the specified file to the server URL provided using an HTTP multipart request.
* @param source Full path of the file on the file system
* @param target URL of the server to receive the file
* @param args JSON Array of args
* @param callbackContext callback id for optional progress reports
*
* args[2] fileKey Name of file request parameter
* args[3] fileName File name to be used on server
* args[4] mimeType Describes file content type
* args[5] params key:value pairs of user-defined parameters
* @return FileUploadResult containing result of upload request
*/
private void upload(final String source, final String target, JSONArray args, CallbackContext callbackContext) throws JSONException {
Log.d(LOG_TAG, "upload " + source + " to " + target);
// Setup the options
final String fileKey = getArgument(args, 2, "file");
final String fileName = getArgument(args, 3, "image.jpg");
final String mimeType = getArgument(args, 4, "image/jpeg");
final JSONObject params = args.optJSONObject(5) == null ? new JSONObject() : args.optJSONObject(5);
final boolean trustEveryone = args.optBoolean(6);
// Always use chunked mode unless set to false as per API
final boolean chunkedMode = args.optBoolean(7) || args.isNull(7);
// Look for headers on the params map for backwards compatibility with older Cordova versions.
final JSONObject headers = args.optJSONObject(8) == null ? params.optJSONObject("headers") : args.optJSONObject(8);
final String objectId = args.getString(9);
final String httpMethod = getArgument(args, 10, "POST");
final CordovaResourceApi resourceApi = webView.getResourceApi();
Log.d(LOG_TAG, "fileKey: " + fileKey);
Log.d(LOG_TAG, "fileName: " + fileName);
Log.d(LOG_TAG, "mimeType: " + mimeType);
Log.d(LOG_TAG, "params: " + params);
Log.d(LOG_TAG, "trustEveryone: " + trustEveryone);
Log.d(LOG_TAG, "chunkedMode: " + chunkedMode);
Log.d(LOG_TAG, "headers: " + headers);
Log.d(LOG_TAG, "objectId: " + objectId);
Log.d(LOG_TAG, "httpMethod: " + httpMethod);
final Uri targetUri = resourceApi.remapUri(Uri.parse(target));
// Accept a path or a URI for the source.
Uri tmpSrc = Uri.parse(source);
final Uri sourceUri = resourceApi.remapUri(
tmpSrc.getScheme() != null ? tmpSrc : Uri.fromFile(new File(source)));
int uriType = CordovaResourceApi.getUriType(targetUri);
final boolean useHttps = uriType == CordovaResourceApi.URI_TYPE_HTTPS;
if (uriType != CordovaResourceApi.URI_TYPE_HTTP && !useHttps) {
JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, null, 0);
Log.e(LOG_TAG, "Unsupported URI: " + targetUri);
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
return;
}
final RequestContext context = new RequestContext(source, target, callbackContext);
synchronized (activeRequests) {
activeRequests.put(objectId, context);
}
cordova.getThreadPool().execute(new Runnable() {
public void run() {
if (context.aborted) {
return;
}
HttpURLConnection conn = null;
HostnameVerifier oldHostnameVerifier = null;
SSLSocketFactory oldSocketFactory = null;
int totalBytes = 0;
int fixedLength = -1;
try {
// Create return object
FileUploadResult result = new FileUploadResult();
FileProgressResult progress = new FileProgressResult();
//------------------ CLIENT REQUEST
// Open a HTTP connection to the URL based on protocol
conn = resourceApi.createHttpConnection(targetUri);
if (useHttps && trustEveryone) {
// Setup the HTTPS connection class to trust everyone
HttpsURLConnection https = (HttpsURLConnection)conn;
oldSocketFactory = trustAllHosts(https);
// Save the current hostnameVerifier
oldHostnameVerifier = https.getHostnameVerifier();
// Setup the connection not to verify hostnames
https.setHostnameVerifier(DO_NOT_VERIFY);
}
// Allow Inputs
conn.setDoInput(true);
// Allow Outputs
conn.setDoOutput(true);
// Don't use a cached copy.
conn.setUseCaches(false);
// Use a post method.
conn.setRequestMethod(httpMethod);
// if we specified a Content-Type header, don't do multipart form upload
boolean multipartFormUpload = (headers == null) || !headers.has("Content-Type");
if (multipartFormUpload) {
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
}
// Set the cookies on the response
String cookie = AmazonCookieManager.getInstance().getCookie(target);
if (cookie != null) {
conn.setRequestProperty("Cookie", cookie);
}
// Handle the other headers
if (headers != null) {
addHeadersToRequest(conn, headers);
}
/*
* Store the non-file portions of the multipart data as a string, so that we can add it
* to the contentSize, since it is part of the body of the HTTP request.
*/
StringBuilder beforeData = new StringBuilder();
try {
for (Iterator<?> iter = params.keys(); iter.hasNext();) {
Object key = iter.next();
if(!String.valueOf(key).equals("headers"))
{
beforeData.append(LINE_START).append(BOUNDARY).append(LINE_END);
beforeData.append("Content-Disposition: form-data; name=\"").append(key.toString()).append('"');
beforeData.append(LINE_END).append(LINE_END);
beforeData.append(params.getString(key.toString()));
beforeData.append(LINE_END);
}
}
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
beforeData.append(LINE_START).append(BOUNDARY).append(LINE_END);
beforeData.append("Content-Disposition: form-data; name=\"").append(fileKey).append("\";");
beforeData.append(" filename=\"").append(fileName).append('"').append(LINE_END);
beforeData.append("Content-Type: ").append(mimeType).append(LINE_END).append(LINE_END);
byte[] beforeDataBytes = beforeData.toString().getBytes("UTF-8");
byte[] tailParamsBytes = (LINE_END + LINE_START + BOUNDARY + LINE_START + LINE_END).getBytes("UTF-8");
// Get a input stream of the file on the phone
OpenForReadResult readResult = resourceApi.openForRead(sourceUri);
int stringLength = beforeDataBytes.length + tailParamsBytes.length;
if (readResult.length >= 0) {
fixedLength = (int)readResult.length;
if (multipartFormUpload)
fixedLength += stringLength;
progress.setLengthComputable(true);
progress.setTotal(fixedLength);
}
Log.d(LOG_TAG, "Content Length: " + fixedLength);
// setFixedLengthStreamingMode causes and OutOfMemoryException on pre-Froyo devices.
// http://code.google.com/p/android/issues/detail?id=3164
// It also causes OOM if HTTPS is used, even on newer devices.
boolean useChunkedMode = chunkedMode && (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO || useHttps);
useChunkedMode = useChunkedMode || (fixedLength == -1);
if (useChunkedMode) {
conn.setChunkedStreamingMode(MAX_BUFFER_SIZE);
// Although setChunkedStreamingMode sets this header, setting it explicitly here works
// around an OutOfMemoryException when using https.
conn.setRequestProperty("Transfer-Encoding", "chunked");
} else {
conn.setFixedLengthStreamingMode(fixedLength);
}
conn.connect();
OutputStream sendStream = null;
try {
sendStream = conn.getOutputStream();
synchronized (context) {
if (context.aborted) {
return;
}
context.currentOutputStream = sendStream;
}
if (multipartFormUpload) {
//We don't want to change encoding, we just want this to write for all Unicode.
sendStream.write(beforeDataBytes);
totalBytes += beforeDataBytes.length;
}
// create a buffer of maximum size
int bytesAvailable = readResult.inputStream.available();
int bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE);
byte[] buffer = new byte[bufferSize];
// read file and write it into form...
int bytesRead = readResult.inputStream.read(buffer, 0, bufferSize);
long prevBytesRead = 0;
while (bytesRead > 0) {
result.setBytesSent(totalBytes);
sendStream.write(buffer, 0, bytesRead);
totalBytes += bytesRead;
if (totalBytes > prevBytesRead + 102400) {
prevBytesRead = totalBytes;
Log.d(LOG_TAG, "Uploaded " + totalBytes + " of " + fixedLength + " bytes");
}
bytesAvailable = readResult.inputStream.available();
bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE);
bytesRead = readResult.inputStream.read(buffer, 0, bufferSize);
// Send a progress event.
progress.setLoaded(totalBytes);
PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
progressResult.setKeepCallback(true);
context.sendPluginResult(progressResult);
}
if (multipartFormUpload) {
// send multipart form data necessary after file data...
sendStream.write(tailParamsBytes);
totalBytes += tailParamsBytes.length;
}
sendStream.flush();
} finally {
safeClose(readResult.inputStream);
safeClose(sendStream);
}
context.currentOutputStream = null;
Log.d(LOG_TAG, "Sent " + totalBytes + " of " + fixedLength);
//------------------ read the SERVER RESPONSE
String responseString;
int responseCode = conn.getResponseCode();
Log.d(LOG_TAG, "response code: " + responseCode);
Log.d(LOG_TAG, "response headers: " + conn.getHeaderFields());
TrackingInputStream inStream = null;
try {
inStream = getInputStream(conn);
synchronized (context) {
if (context.aborted) {
return;
}
context.currentInputStream = inStream;
}
ByteArrayOutputStream out = new ByteArrayOutputStream(Math.max(1024, conn.getContentLength()));
byte[] buffer = new byte[1024];
int bytesRead = 0;
// write bytes to file
while ((bytesRead = inStream.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
responseString = out.toString("UTF-8");
} finally {
context.currentInputStream = null;
safeClose(inStream);
}
Log.d(LOG_TAG, "got response from server");
Log.d(LOG_TAG, responseString.substring(0, Math.min(256, responseString.length())));
// send request and retrieve response
result.setResponseCode(responseCode);
result.setResponse(responseString);
context.sendPluginResult(new PluginResult(PluginResult.Status.OK, result.toJSONObject()));
} catch (FileNotFoundException e) {
JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target, conn);
Log.e(LOG_TAG, error.toString(), e);
context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
} catch (IOException e) {
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn);
Log.e(LOG_TAG, error.toString(), e);
Log.e(LOG_TAG, "Failed after uploading " + totalBytes + " of " + fixedLength + " bytes.");
context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
context.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
} catch (Throwable t) {
// Shouldn't happen, but will
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn);
Log.e(LOG_TAG, error.toString(), t);
context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
} finally {
synchronized (activeRequests) {
activeRequests.remove(objectId);
}
if (conn != null) {
// Revert back to the proper verifier and socket factories
// Revert back to the proper verifier and socket factories
if (trustEveryone && useHttps) {
HttpsURLConnection https = (HttpsURLConnection) conn;
https.setHostnameVerifier(oldHostnameVerifier);
https.setSSLSocketFactory(oldSocketFactory);
}
}
}
}
});
}
private static void safeClose(Closeable stream) {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
}
}
}
private static TrackingInputStream getInputStream(URLConnection conn) throws IOException {
String encoding = conn.getContentEncoding();
if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
return new TrackingGZIPInputStream(new ExposedGZIPInputStream(conn.getInputStream()));
}
return new SimpleTrackingInputStream(conn.getInputStream());
}
// always verify the host - don't check for certificate
private static final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// Create a trust manager that does not validate certificate chains
private static final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[] {};
}
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
} };
/**
* This function will install a trust manager that will blindly trust all SSL
* certificates. The reason this code is being added is to enable developers
* to do development using self signed SSL certificates on their web server.
*
* The standard HttpsURLConnection class will throw an exception on self
* signed certificates if this code is not run.
*/
private static SSLSocketFactory trustAllHosts(HttpsURLConnection connection) {
// Install the all-trusting trust manager
SSLSocketFactory oldFactory = connection.getSSLSocketFactory();
try {
// Install our all trusting manager
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
SSLSocketFactory newFactory = sc.getSocketFactory();
connection.setSSLSocketFactory(newFactory);
} catch (Exception e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
return oldFactory;
}
private static JSONObject createFileTransferError(int errorCode, String source, String target, URLConnection connection) {
int httpStatus = 0;
StringBuilder bodyBuilder = new StringBuilder();
String body = null;
if (connection != null) {
try {
if (connection instanceof HttpURLConnection) {
httpStatus = ((HttpURLConnection)connection).getResponseCode();
InputStream err = ((HttpURLConnection) connection).getErrorStream();
if(err != null)
{
BufferedReader reader = new BufferedReader(new InputStreamReader(err, "UTF-8"));
try {
String line = reader.readLine();
while(line != null) {
bodyBuilder.append(line);
line = reader.readLine();
if(line != null) {
bodyBuilder.append('\n');
}
}
body = bodyBuilder.toString();
} finally {
reader.close();
}
}
}
// IOException can leave connection object in a bad state, so catch all exceptions.
} catch (Throwable e) {
Log.w(LOG_TAG, "Error getting HTTP status code from connection.", e);
}
}
return createFileTransferError(errorCode, source, target, body, httpStatus);
}
/**
* Create an error object based on the passed in errorCode
* @param errorCode the error
* @return JSONObject containing the error
*/
private static JSONObject createFileTransferError(int errorCode, String source, String target, String body, Integer httpStatus) {
JSONObject error = null;
try {
error = new JSONObject();
error.put("code", errorCode);
error.put("source", source);
error.put("target", target);
if(body != null)
{
error.put("body", body);
}
if (httpStatus != null) {
error.put("http_status", httpStatus);
}
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
return error;
}
/**
* Convenience method to read a parameter from the list of JSON args.
* @param args the args passed to the Plugin
* @param position the position to retrieve the arg from
* @param defaultString the default to be used if the arg does not exist
* @return String with the retrieved value
*/
private static String getArgument(JSONArray args, int position, String defaultString) {
String arg = defaultString;
if (args.length() > position) {
arg = args.optString(position);
if (arg == null || "null".equals(arg)) {
arg = defaultString;
}
}
return arg;
}
/**
* Downloads a file form a given URL and saves it to the specified directory.
*
* @param source URL of the server to receive the file
* @param target Full path of the file on the file system
*/
private void download(final String source, final String target, JSONArray args, CallbackContext callbackContext) throws JSONException {
Log.d(LOG_TAG, "download " + source + " to " + target);
final CordovaResourceApi resourceApi = webView.getResourceApi();
final boolean trustEveryone = args.optBoolean(2);
final String objectId = args.getString(3);
final JSONObject headers = args.optJSONObject(4);
final Uri sourceUri = resourceApi.remapUri(Uri.parse(source));
// Accept a path or a URI for the source.
Uri tmpTarget = Uri.parse(target);
final Uri targetUri = resourceApi.remapUri(
tmpTarget.getScheme() != null ? tmpTarget : Uri.fromFile(new File(target)));
int uriType = CordovaResourceApi.getUriType(sourceUri);
final boolean useHttps = uriType == CordovaResourceApi.URI_TYPE_HTTPS;
final boolean isLocalTransfer = !useHttps && uriType != CordovaResourceApi.URI_TYPE_HTTP;
if (uriType == CordovaResourceApi.URI_TYPE_UNKNOWN) {
JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, null, 0);
Log.e(LOG_TAG, "Unsupported URI: " + targetUri);
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
return;
}
// TODO: refactor to also allow resources & content:
if (!isLocalTransfer && !Config.isUrlWhiteListed(source)) {
Log.w(LOG_TAG, "Source URL is not in white list: '" + source + "'");
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, null, 401);
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
return;
}
final RequestContext context = new RequestContext(source, target, callbackContext);
synchronized (activeRequests) {
activeRequests.put(objectId, context);
}
cordova.getThreadPool().execute(new Runnable() {
public void run() {
if (context.aborted) {
return;
}
HttpURLConnection connection = null;
HostnameVerifier oldHostnameVerifier = null;
SSLSocketFactory oldSocketFactory = null;
File file = null;
PluginResult result = null;
TrackingInputStream inputStream = null;
OutputStream outputStream = null;
try {
OpenForReadResult readResult = null;
outputStream = resourceApi.openOutputStream(targetUri);
file = resourceApi.mapUriToFile(targetUri);
context.targetFile = file;
Log.d(LOG_TAG, "Download file:" + sourceUri);
FileProgressResult progress = new FileProgressResult();
if (isLocalTransfer) {
readResult = resourceApi.openForRead(sourceUri);
if (readResult.length != -1) {
progress.setLengthComputable(true);
progress.setTotal(readResult.length);
}
inputStream = new SimpleTrackingInputStream(readResult.inputStream);
} else {
// connect to server
// Open a HTTP connection to the URL based on protocol
connection = resourceApi.createHttpConnection(sourceUri);
if (useHttps && trustEveryone) {
// Setup the HTTPS connection class to trust everyone
HttpsURLConnection https = (HttpsURLConnection)connection;
oldSocketFactory = trustAllHosts(https);
// Save the current hostnameVerifier
oldHostnameVerifier = https.getHostnameVerifier();
// Setup the connection not to verify hostnames
https.setHostnameVerifier(DO_NOT_VERIFY);
}
connection.setRequestMethod("GET");
// TODO: Make OkHttp use this AmazonCookieManager by default.
String cookie = AmazonCookieManager.getInstance().getCookie(sourceUri.toString());
if(cookie != null)
{
connection.setRequestProperty("cookie", cookie);
}
// This must be explicitly set for gzip progress tracking to work.
connection.setRequestProperty("Accept-Encoding", "gzip");
// Handle the other headers
if (headers != null) {
addHeadersToRequest(connection, headers);
}
connection.connect();
if (connection.getContentEncoding() == null || connection.getContentEncoding().equalsIgnoreCase("gzip")) {
// Only trust content-length header if we understand
// the encoding -- identity or gzip
if (connection.getContentLength() != -1) {
progress.setLengthComputable(true);
progress.setTotal(connection.getContentLength());
}
}
inputStream = getInputStream(connection);
}
try {
synchronized (context) {
if (context.aborted) {
return;
}
context.currentInputStream = inputStream;
}
// write bytes to file
byte[] buffer = new byte[MAX_BUFFER_SIZE];
int bytesRead = 0;
while ((bytesRead = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, bytesRead);
// Send a progress event.
progress.setLoaded(inputStream.getTotalRawBytesRead());
PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
progressResult.setKeepCallback(true);
context.sendPluginResult(progressResult);
}
} finally {
context.currentInputStream = null;
safeClose(inputStream);
safeClose(outputStream);
}
Log.d(LOG_TAG, "Saved file: " + target);
// create FileEntry object
FileUtils filePlugin = (FileUtils)webView.pluginManager.getPlugin("File");
if (filePlugin != null) {
JSONObject fileEntry = filePlugin.getEntryForFile(file);
if (fileEntry != null) {
result = new PluginResult(PluginResult.Status.OK, fileEntry);
} else {
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, connection);
Log.e(LOG_TAG, "File plugin cannot represent download path");
result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
}
} else {
Log.e(LOG_TAG, "File plugin not found; cannot save downloaded file");
result = new PluginResult(PluginResult.Status.ERROR, "File plugin not found; cannot save downloaded file");
}
} catch (FileNotFoundException e) {
JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target, connection);
Log.e(LOG_TAG, error.toString(), e);
result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} catch (IOException e) {
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, connection);
Log.e(LOG_TAG, error.toString(), e);
result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
result = new PluginResult(PluginResult.Status.JSON_EXCEPTION);
} catch (Throwable e) {
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, connection);
Log.e(LOG_TAG, error.toString(), e);
result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} finally {
safeClose(outputStream);
synchronized (activeRequests) {
activeRequests.remove(objectId);
}
if (connection != null) {
// Revert back to the proper verifier and socket factories
if (trustEveryone && useHttps) {
HttpsURLConnection https = (HttpsURLConnection) connection;
https.setHostnameVerifier(oldHostnameVerifier);
https.setSSLSocketFactory(oldSocketFactory);
}
}
if (result == null) {
result = new PluginResult(PluginResult.Status.ERROR, createFileTransferError(CONNECTION_ERR, source, target, connection));
}
// Remove incomplete download.
if (result.getStatus() != PluginResult.Status.OK.ordinal() && file != null) {
file.delete();
}
context.sendPluginResult(result);
}
}
});
}
/**
* Abort an ongoing upload or download.
*/
private void abort(String objectId) {
final RequestContext context;
synchronized (activeRequests) {
context = activeRequests.remove(objectId);
}
if (context != null) {
File file = context.targetFile;
if (file != null) {
file.delete();
}
// Trigger the abort callback immediately to minimize latency between it and abort() being called.
JSONObject error = createFileTransferError(ABORTED_ERR, context.source, context.target, null, -1);
synchronized (context) {
context.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, error));
context.aborted = true;
}
// Closing the streams can block, so execute on a background thread.
cordova.getThreadPool().execute(new Runnable() {
public void run() {
synchronized (context) {
safeClose(context.currentInputStream);
safeClose(context.currentOutputStream);
}
}
});
}
}
}

View File

@@ -0,0 +1,63 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova.filetransfer;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Encapsulates in-progress status of uploading or downloading a file to a remote server.
*/
public class FileProgressResult {
private boolean lengthComputable = false; // declares whether total is known
private long loaded = 0; // bytes sent so far
private long total = 0; // bytes total, if known
public boolean getLengthComputable() {
return lengthComputable;
}
public void setLengthComputable(boolean computable) {
this.lengthComputable = computable;
}
public long getLoaded() {
return loaded;
}
public void setLoaded(long bytes) {
this.loaded = bytes;
}
public long getTotal() {
return total;
}
public void setTotal(long bytes) {
this.total = bytes;
}
public JSONObject toJSONObject() throws JSONException {
return new JSONObject(
"{loaded:" + loaded +
",total:" + total +
",lengthComputable:" + (lengthComputable ? "true" : "false") + "}");
}
}

View File

@@ -0,0 +1,921 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova.filetransfer;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaResourceApi;
import org.apache.cordova.CordovaResourceApi.OpenForReadResult;
import org.apache.cordova.LOG;
import org.apache.cordova.PluginManager;
import org.apache.cordova.PluginResult;
import org.apache.cordova.file.FileUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.net.Uri;
import android.os.Build;
import android.webkit.CookieManager;
public class FileTransfer extends CordovaPlugin {
private static final String LOG_TAG = "FileTransfer";
private static final String LINE_START = "--";
private static final String LINE_END = "\r\n";
private static final String BOUNDARY = "+++++";
public static int FILE_NOT_FOUND_ERR = 1;
public static int INVALID_URL_ERR = 2;
public static int CONNECTION_ERR = 3;
public static int ABORTED_ERR = 4;
public static int NOT_MODIFIED_ERR = 5;
private static HashMap<String, RequestContext> activeRequests = new HashMap<String, RequestContext>();
private static final int MAX_BUFFER_SIZE = 16 * 1024;
private static final class RequestContext {
String source;
String target;
File targetFile;
CallbackContext callbackContext;
HttpURLConnection connection;
boolean aborted;
RequestContext(String source, String target, CallbackContext callbackContext) {
this.source = source;
this.target = target;
this.callbackContext = callbackContext;
}
void sendPluginResult(PluginResult pluginResult) {
synchronized (this) {
if (!aborted) {
callbackContext.sendPluginResult(pluginResult);
}
}
}
}
/**
* Adds an interface method to an InputStream to return the number of bytes
* read from the raw stream. This is used to track total progress against
* the HTTP Content-Length header value from the server.
*/
private static abstract class TrackingInputStream extends FilterInputStream {
public TrackingInputStream(final InputStream in) {
super(in);
}
public abstract long getTotalRawBytesRead();
}
private static class ExposedGZIPInputStream extends GZIPInputStream {
public ExposedGZIPInputStream(final InputStream in) throws IOException {
super(in);
}
public Inflater getInflater() {
return inf;
}
}
/**
* Provides raw bytes-read tracking for a GZIP input stream. Reports the
* total number of compressed bytes read from the input, rather than the
* number of uncompressed bytes.
*/
private static class TrackingGZIPInputStream extends TrackingInputStream {
private ExposedGZIPInputStream gzin;
public TrackingGZIPInputStream(final ExposedGZIPInputStream gzin) throws IOException {
super(gzin);
this.gzin = gzin;
}
public long getTotalRawBytesRead() {
return gzin.getInflater().getBytesRead();
}
}
/**
* Provides simple total-bytes-read tracking for an existing InputStream
*/
private static class SimpleTrackingInputStream extends TrackingInputStream {
private long bytesRead = 0;
public SimpleTrackingInputStream(InputStream stream) {
super(stream);
}
private int updateBytesRead(int newBytesRead) {
if (newBytesRead != -1) {
bytesRead += newBytesRead;
}
return newBytesRead;
}
@Override
public int read() throws IOException {
return updateBytesRead(super.read());
}
// Note: FilterInputStream delegates read(byte[] bytes) to the below method,
// so we don't override it or else double count (CB-5631).
@Override
public int read(byte[] bytes, int offset, int count) throws IOException {
return updateBytesRead(super.read(bytes, offset, count));
}
public long getTotalRawBytesRead() {
return bytesRead;
}
}
@Override
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
if (action.equals("upload") || action.equals("download")) {
String source = args.getString(0);
String target = args.getString(1);
if (action.equals("upload")) {
upload(source, target, args, callbackContext);
} else {
download(source, target, args, callbackContext);
}
return true;
} else if (action.equals("abort")) {
String objectId = args.getString(0);
abort(objectId);
callbackContext.success();
return true;
}
return false;
}
private static void addHeadersToRequest(URLConnection connection, JSONObject headers) {
try {
for (Iterator<?> iter = headers.keys(); iter.hasNext(); ) {
/* RFC 2616 says that non-ASCII characters and control
* characters are not allowed in header names or values.
* Additionally, spaces are not allowed in header names.
* RFC 2046 Quoted-printable encoding may be used to encode
* arbitrary characters, but we donon- not do that encoding here.
*/
String headerKey = iter.next().toString();
String cleanHeaderKey = headerKey.replaceAll("\\n","")
.replaceAll("\\s+","")
.replaceAll(":", "")
.replaceAll("[^\\x20-\\x7E]+", "");
JSONArray headerValues = headers.optJSONArray(headerKey);
if (headerValues == null) {
headerValues = new JSONArray();
/* RFC 2616 also says that any amount of consecutive linear
* whitespace within a header value can be replaced with a
* single space character, without affecting the meaning of
* that value.
*/
String headerValue = headers.getString(headerKey);
String finalValue = headerValue.replaceAll("\\s+", " ").replaceAll("\\n"," ").replaceAll("[^\\x20-\\x7E]+", " ");
headerValues.put(finalValue);
}
//Use the clean header key, not the one that we passed in
connection.setRequestProperty(cleanHeaderKey, headerValues.getString(0));
for (int i = 1; i < headerValues.length(); ++i) {
connection.addRequestProperty(headerKey, headerValues.getString(i));
}
}
} catch (JSONException e1) {
// No headers to be manipulated!
}
}
private String getCookies(final String target) {
boolean gotCookie = false;
String cookie = null;
Class webViewClass = webView.getClass();
try {
Method gcmMethod = webViewClass.getMethod("getCookieManager");
Class iccmClass = gcmMethod.getReturnType();
Method gcMethod = iccmClass.getMethod("getCookie", String.class);
cookie = (String)gcMethod.invoke(
iccmClass.cast(
gcmMethod.invoke(webView)
), target);
gotCookie = true;
} catch (NoSuchMethodException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
} catch (ClassCastException e) {
}
if (!gotCookie && CookieManager.getInstance() != null) {
cookie = CookieManager.getInstance().getCookie(target);
}
return cookie;
}
/**
* Uploads the specified file to the server URL provided using an HTTP multipart request.
* @param source Full path of the file on the file system
* @param target URL of the server to receive the file
* @param args JSON Array of args
* @param callbackContext callback id for optional progress reports
*
* args[2] fileKey Name of file request parameter
* args[3] fileName File name to be used on server
* args[4] mimeType Describes file content type
* args[5] params key:value pairs of user-defined parameters
* @return FileUploadResult containing result of upload request
*/
private void upload(final String source, final String target, JSONArray args, CallbackContext callbackContext) throws JSONException {
LOG.d(LOG_TAG, "upload " + source + " to " + target);
// Setup the options
final String fileKey = getArgument(args, 2, "file");
final String fileName = getArgument(args, 3, "image.jpg");
final String mimeType = getArgument(args, 4, "image/jpeg");
final JSONObject params = args.optJSONObject(5) == null ? new JSONObject() : args.optJSONObject(5);
// Always use chunked mode unless set to false as per API
final boolean chunkedMode = args.optBoolean(7) || args.isNull(7);
// Look for headers on the params map for backwards compatibility with older Cordova versions.
final JSONObject headers = args.optJSONObject(8) == null ? params.optJSONObject("headers") : args.optJSONObject(8);
final String objectId = args.getString(9);
final String httpMethod = getArgument(args, 10, "POST");
final CordovaResourceApi resourceApi = webView.getResourceApi();
LOG.d(LOG_TAG, "fileKey: " + fileKey);
LOG.d(LOG_TAG, "fileName: " + fileName);
LOG.d(LOG_TAG, "mimeType: " + mimeType);
LOG.d(LOG_TAG, "params: " + params);
LOG.d(LOG_TAG, "chunkedMode: " + chunkedMode);
LOG.d(LOG_TAG, "headers: " + headers);
LOG.d(LOG_TAG, "objectId: " + objectId);
LOG.d(LOG_TAG, "httpMethod: " + httpMethod);
final Uri targetUri = resourceApi.remapUri(Uri.parse(target));
int uriType = CordovaResourceApi.getUriType(targetUri);
final boolean useHttps = uriType == CordovaResourceApi.URI_TYPE_HTTPS;
if (uriType != CordovaResourceApi.URI_TYPE_HTTP && !useHttps) {
JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, null, 0, null);
LOG.e(LOG_TAG, "Unsupported URI: " + targetUri);
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
return;
}
final RequestContext context = new RequestContext(source, target, callbackContext);
synchronized (activeRequests) {
activeRequests.put(objectId, context);
}
cordova.getThreadPool().execute(new Runnable() {
public void run() {
if (context.aborted) {
return;
}
// We should call remapUri on background thread otherwise it throws
// IllegalStateException when trying to remap 'cdvfile://localhost/content/...' URIs
// via ContentFilesystem (see https://issues.apache.org/jira/browse/CB-9022)
Uri tmpSrc = Uri.parse(source);
final Uri sourceUri = resourceApi.remapUri(
tmpSrc.getScheme() != null ? tmpSrc : Uri.fromFile(new File(source)));
HttpURLConnection conn = null;
int totalBytes = 0;
int fixedLength = -1;
try {
// Create return object
FileUploadResult result = new FileUploadResult();
FileProgressResult progress = new FileProgressResult();
//------------------ CLIENT REQUEST
// Open a HTTP connection to the URL based on protocol
conn = resourceApi.createHttpConnection(targetUri);
// Allow Inputs
conn.setDoInput(true);
// Allow Outputs
conn.setDoOutput(true);
// Don't use a cached copy.
conn.setUseCaches(false);
// Use a post method.
conn.setRequestMethod(httpMethod);
// if we specified a Content-Type header, don't do multipart form upload
boolean multipartFormUpload = (headers == null) || !headers.has("Content-Type");
if (multipartFormUpload) {
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
}
// Set the cookies on the response
String cookie = getCookies(target);
if (cookie != null) {
conn.setRequestProperty("Cookie", cookie);
}
// Handle the other headers
if (headers != null) {
addHeadersToRequest(conn, headers);
}
/*
* Store the non-file portions of the multipart data as a string, so that we can add it
* to the contentSize, since it is part of the body of the HTTP request.
*/
StringBuilder beforeData = new StringBuilder();
try {
for (Iterator<?> iter = params.keys(); iter.hasNext();) {
Object key = iter.next();
if(!String.valueOf(key).equals("headers"))
{
beforeData.append(LINE_START).append(BOUNDARY).append(LINE_END);
beforeData.append("Content-Disposition: form-data; name=\"").append(key.toString()).append('"');
beforeData.append(LINE_END).append(LINE_END);
beforeData.append(params.getString(key.toString()));
beforeData.append(LINE_END);
}
}
} catch (JSONException e) {
LOG.e(LOG_TAG, e.getMessage(), e);
}
beforeData.append(LINE_START).append(BOUNDARY).append(LINE_END);
beforeData.append("Content-Disposition: form-data; name=\"").append(fileKey).append("\";");
beforeData.append(" filename=\"").append(fileName).append('"').append(LINE_END);
beforeData.append("Content-Type: ").append(mimeType).append(LINE_END).append(LINE_END);
byte[] beforeDataBytes = beforeData.toString().getBytes("UTF-8");
byte[] tailParamsBytes = (LINE_END + LINE_START + BOUNDARY + LINE_START + LINE_END).getBytes("UTF-8");
// Get a input stream of the file on the phone
OpenForReadResult readResult = resourceApi.openForRead(sourceUri);
int stringLength = beforeDataBytes.length + tailParamsBytes.length;
if (readResult.length >= 0) {
fixedLength = (int)readResult.length;
if (multipartFormUpload)
fixedLength += stringLength;
progress.setLengthComputable(true);
progress.setTotal(fixedLength);
}
LOG.d(LOG_TAG, "Content Length: " + fixedLength);
// setFixedLengthStreamingMode causes and OutOfMemoryException on pre-Froyo devices.
// http://code.google.com/p/android/issues/detail?id=3164
// It also causes OOM if HTTPS is used, even on newer devices.
boolean useChunkedMode = chunkedMode || (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO);
useChunkedMode = useChunkedMode || (fixedLength == -1);
if (useChunkedMode) {
conn.setChunkedStreamingMode(MAX_BUFFER_SIZE);
// Although setChunkedStreamingMode sets this header, setting it explicitly here works
// around an OutOfMemoryException when using https.
conn.setRequestProperty("Transfer-Encoding", "chunked");
} else {
conn.setFixedLengthStreamingMode(fixedLength);
if (useHttps) {
LOG.w(LOG_TAG, "setFixedLengthStreamingMode could cause OutOfMemoryException - switch to chunkedMode=true to avoid it if this is an issue.");
}
}
conn.connect();
OutputStream sendStream = null;
try {
sendStream = conn.getOutputStream();
synchronized (context) {
if (context.aborted) {
return;
}
context.connection = conn;
}
if (multipartFormUpload) {
//We don't want to change encoding, we just want this to write for all Unicode.
sendStream.write(beforeDataBytes);
totalBytes += beforeDataBytes.length;
}
// create a buffer of maximum size
int bytesAvailable = readResult.inputStream.available();
int bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE);
byte[] buffer = new byte[bufferSize];
// read file and write it into form...
int bytesRead = readResult.inputStream.read(buffer, 0, bufferSize);
long prevBytesRead = 0;
while (bytesRead > 0) {
totalBytes += bytesRead;
result.setBytesSent(totalBytes);
sendStream.write(buffer, 0, bytesRead);
if (totalBytes > prevBytesRead + 102400) {
prevBytesRead = totalBytes;
LOG.d(LOG_TAG, "Uploaded " + totalBytes + " of " + fixedLength + " bytes");
}
bytesAvailable = readResult.inputStream.available();
bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE);
bytesRead = readResult.inputStream.read(buffer, 0, bufferSize);
// Send a progress event.
progress.setLoaded(totalBytes);
PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
progressResult.setKeepCallback(true);
context.sendPluginResult(progressResult);
}
if (multipartFormUpload) {
// send multipart form data necessary after file data...
sendStream.write(tailParamsBytes);
totalBytes += tailParamsBytes.length;
}
sendStream.flush();
} finally {
safeClose(readResult.inputStream);
safeClose(sendStream);
}
synchronized (context) {
context.connection = null;
}
LOG.d(LOG_TAG, "Sent " + totalBytes + " of " + fixedLength);
//------------------ read the SERVER RESPONSE
String responseString;
int responseCode = conn.getResponseCode();
LOG.d(LOG_TAG, "response code: " + responseCode);
LOG.d(LOG_TAG, "response headers: " + conn.getHeaderFields());
TrackingInputStream inStream = null;
try {
inStream = getInputStream(conn);
synchronized (context) {
if (context.aborted) {
return;
}
context.connection = conn;
}
ByteArrayOutputStream out = new ByteArrayOutputStream(Math.max(1024, conn.getContentLength()));
byte[] buffer = new byte[1024];
int bytesRead = 0;
// write bytes to file
while ((bytesRead = inStream.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
responseString = out.toString("UTF-8");
} finally {
synchronized (context) {
context.connection = null;
}
safeClose(inStream);
}
LOG.d(LOG_TAG, "got response from server");
LOG.d(LOG_TAG, responseString.substring(0, Math.min(256, responseString.length())));
// send request and retrieve response
result.setResponseCode(responseCode);
result.setResponse(responseString);
context.sendPluginResult(new PluginResult(PluginResult.Status.OK, result.toJSONObject()));
} catch (FileNotFoundException e) {
JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target, conn, e);
LOG.e(LOG_TAG, error.toString(), e);
context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
} catch (IOException e) {
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn, e);
LOG.e(LOG_TAG, error.toString(), e);
LOG.e(LOG_TAG, "Failed after uploading " + totalBytes + " of " + fixedLength + " bytes.");
context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
} catch (JSONException e) {
LOG.e(LOG_TAG, e.getMessage(), e);
context.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
} catch (Throwable t) {
// Shouldn't happen, but will
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn, t);
LOG.e(LOG_TAG, error.toString(), t);
context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
} finally {
synchronized (activeRequests) {
activeRequests.remove(objectId);
}
}
}
});
}
private static void safeClose(Closeable stream) {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
}
}
}
private static TrackingInputStream getInputStream(URLConnection conn) throws IOException {
String encoding = conn.getContentEncoding();
if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
return new TrackingGZIPInputStream(new ExposedGZIPInputStream(conn.getInputStream()));
}
return new SimpleTrackingInputStream(conn.getInputStream());
}
private static JSONObject createFileTransferError(int errorCode, String source, String target, URLConnection connection, Throwable throwable) {
int httpStatus = 0;
StringBuilder bodyBuilder = new StringBuilder();
String body = null;
if (connection != null) {
try {
if (connection instanceof HttpURLConnection) {
httpStatus = ((HttpURLConnection)connection).getResponseCode();
InputStream err = ((HttpURLConnection) connection).getErrorStream();
if(err != null)
{
BufferedReader reader = new BufferedReader(new InputStreamReader(err, "UTF-8"));
try {
String line = reader.readLine();
while(line != null) {
bodyBuilder.append(line);
line = reader.readLine();
if(line != null) {
bodyBuilder.append('\n');
}
}
body = bodyBuilder.toString();
} finally {
reader.close();
}
}
}
// IOException can leave connection object in a bad state, so catch all exceptions.
} catch (Throwable e) {
LOG.w(LOG_TAG, "Error getting HTTP status code from connection.", e);
}
}
return createFileTransferError(errorCode, source, target, body, httpStatus, throwable);
}
/**
* Create an error object based on the passed in errorCode
* @param errorCode the error
* @return JSONObject containing the error
*/
private static JSONObject createFileTransferError(int errorCode, String source, String target, String body, Integer httpStatus, Throwable throwable) {
JSONObject error = null;
try {
error = new JSONObject();
error.put("code", errorCode);
error.put("source", source);
error.put("target", target);
if(body != null)
{
error.put("body", body);
}
if (httpStatus != null) {
error.put("http_status", httpStatus);
}
if (throwable != null) {
String msg = throwable.getMessage();
if (msg == null || "".equals(msg)) {
msg = throwable.toString();
}
error.put("exception", msg);
}
} catch (JSONException e) {
LOG.e(LOG_TAG, e.getMessage(), e);
}
return error;
}
/**
* Convenience method to read a parameter from the list of JSON args.
* @param args the args passed to the Plugin
* @param position the position to retrieve the arg from
* @param defaultString the default to be used if the arg does not exist
* @return String with the retrieved value
*/
private static String getArgument(JSONArray args, int position, String defaultString) {
String arg = defaultString;
if (args.length() > position) {
arg = args.optString(position);
if (arg == null || "null".equals(arg)) {
arg = defaultString;
}
}
return arg;
}
/**
* Downloads a file form a given URL and saves it to the specified directory.
*
* @param source URL of the server to receive the file
* @param target Full path of the file on the file system
*/
private void download(final String source, final String target, JSONArray args, CallbackContext callbackContext) throws JSONException {
LOG.d(LOG_TAG, "download " + source + " to " + target);
final CordovaResourceApi resourceApi = webView.getResourceApi();
final String objectId = args.getString(3);
final JSONObject headers = args.optJSONObject(4);
final Uri sourceUri = resourceApi.remapUri(Uri.parse(source));
int uriType = CordovaResourceApi.getUriType(sourceUri);
final boolean useHttps = uriType == CordovaResourceApi.URI_TYPE_HTTPS;
final boolean isLocalTransfer = !useHttps && uriType != CordovaResourceApi.URI_TYPE_HTTP;
if (uriType == CordovaResourceApi.URI_TYPE_UNKNOWN) {
JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, null, 0, null);
LOG.e(LOG_TAG, "Unsupported URI: " + sourceUri);
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
return;
}
/* This code exists for compatibility between 3.x and 4.x versions of Cordova.
* Previously the CordovaWebView class had a method, getWhitelist, which would
* return a Whitelist object. Since the fixed whitelist is removed in Cordova 4.x,
* the correct call now is to shouldAllowRequest from the plugin manager.
*/
Boolean shouldAllowRequest = null;
if (isLocalTransfer) {
shouldAllowRequest = true;
}
if (shouldAllowRequest == null) {
try {
Method gpm = webView.getClass().getMethod("getPluginManager");
PluginManager pm = (PluginManager)gpm.invoke(webView);
Method san = pm.getClass().getMethod("shouldAllowRequest", String.class);
shouldAllowRequest = (Boolean)san.invoke(pm, source);
} catch (NoSuchMethodException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
}
if (!Boolean.TRUE.equals(shouldAllowRequest)) {
LOG.w(LOG_TAG, "Source URL is not in white list: '" + source + "'");
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, null, 401, null);
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
return;
}
final RequestContext context = new RequestContext(source, target, callbackContext);
synchronized (activeRequests) {
activeRequests.put(objectId, context);
}
cordova.getThreadPool().execute(new Runnable() {
public void run() {
if (context.aborted) {
return;
}
// Accept a path or a URI for the source.
Uri tmpTarget = Uri.parse(target);
Uri targetUri = resourceApi.remapUri(
tmpTarget.getScheme() != null ? tmpTarget : Uri.fromFile(new File(target)));
HttpURLConnection connection = null;
File file = null;
PluginResult result = null;
TrackingInputStream inputStream = null;
boolean cached = false;
OutputStream outputStream = null;
try {
OpenForReadResult readResult = null;
file = resourceApi.mapUriToFile(targetUri);
context.targetFile = file;
LOG.d(LOG_TAG, "Download file:" + sourceUri);
FileProgressResult progress = new FileProgressResult();
if (isLocalTransfer) {
readResult = resourceApi.openForRead(sourceUri);
if (readResult.length != -1) {
progress.setLengthComputable(true);
progress.setTotal(readResult.length);
}
inputStream = new SimpleTrackingInputStream(readResult.inputStream);
} else {
// connect to server
// Open a HTTP connection to the URL based on protocol
connection = resourceApi.createHttpConnection(sourceUri);
connection.setRequestMethod("GET");
// TODO: Make OkHttp use this CookieManager by default.
String cookie = getCookies(sourceUri.toString());
if(cookie != null)
{
connection.setRequestProperty("cookie", cookie);
}
// This must be explicitly set for gzip progress tracking to work.
connection.setRequestProperty("Accept-Encoding", "gzip");
// Handle the other headers
if (headers != null) {
addHeadersToRequest(connection, headers);
}
connection.connect();
if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
cached = true;
connection.disconnect();
LOG.d(LOG_TAG, "Resource not modified: " + source);
JSONObject error = createFileTransferError(NOT_MODIFIED_ERR, source, target, connection, null);
result = new PluginResult(PluginResult.Status.ERROR, error);
} else {
if (connection.getContentEncoding() == null || connection.getContentEncoding().equalsIgnoreCase("gzip")) {
// Only trust content-length header if we understand
// the encoding -- identity or gzip
if (connection.getContentLength() != -1) {
progress.setLengthComputable(true);
progress.setTotal(connection.getContentLength());
}
}
inputStream = getInputStream(connection);
}
}
if (!cached) {
try {
synchronized (context) {
if (context.aborted) {
return;
}
context.connection = connection;
}
// write bytes to file
byte[] buffer = new byte[MAX_BUFFER_SIZE];
int bytesRead = 0;
outputStream = resourceApi.openOutputStream(targetUri);
while ((bytesRead = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, bytesRead);
// Send a progress event.
progress.setLoaded(inputStream.getTotalRawBytesRead());
PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
progressResult.setKeepCallback(true);
context.sendPluginResult(progressResult);
}
} finally {
synchronized (context) {
context.connection = null;
}
safeClose(inputStream);
safeClose(outputStream);
}
LOG.d(LOG_TAG, "Saved file: " + target);
// create FileEntry object
Class webViewClass = webView.getClass();
PluginManager pm = null;
try {
Method gpm = webViewClass.getMethod("getPluginManager");
pm = (PluginManager) gpm.invoke(webView);
} catch (NoSuchMethodException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
if (pm == null) {
try {
Field pmf = webViewClass.getField("pluginManager");
pm = (PluginManager)pmf.get(webView);
} catch (NoSuchFieldException e) {
} catch (IllegalAccessException e) {
}
}
file = resourceApi.mapUriToFile(targetUri);
context.targetFile = file;
FileUtils filePlugin = (FileUtils) pm.getPlugin("File");
if (filePlugin != null) {
JSONObject fileEntry = filePlugin.getEntryForFile(file);
if (fileEntry != null) {
result = new PluginResult(PluginResult.Status.OK, fileEntry);
} else {
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, connection, null);
LOG.e(LOG_TAG, "File plugin cannot represent download path");
result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
}
} else {
LOG.e(LOG_TAG, "File plugin not found; cannot save downloaded file");
result = new PluginResult(PluginResult.Status.ERROR, "File plugin not found; cannot save downloaded file");
}
}
} catch (FileNotFoundException e) {
JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target, connection, e);
LOG.e(LOG_TAG, error.toString(), e);
result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} catch (IOException e) {
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, connection, e);
LOG.e(LOG_TAG, error.toString(), e);
result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} catch (JSONException e) {
LOG.e(LOG_TAG, e.getMessage(), e);
result = new PluginResult(PluginResult.Status.JSON_EXCEPTION);
} catch (Throwable e) {
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, connection, e);
LOG.e(LOG_TAG, error.toString(), e);
result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} finally {
synchronized (activeRequests) {
activeRequests.remove(objectId);
}
if (result == null) {
result = new PluginResult(PluginResult.Status.ERROR, createFileTransferError(CONNECTION_ERR, source, target, connection, null));
}
// Remove incomplete download.
if (!cached && result.getStatus() != PluginResult.Status.OK.ordinal() && file != null) {
file.delete();
}
context.sendPluginResult(result);
}
}
});
}
/**
* Abort an ongoing upload or download.
*/
private void abort(String objectId) {
final RequestContext context;
synchronized (activeRequests) {
context = activeRequests.remove(objectId);
}
if (context != null) {
// Closing the streams can block, so execute on a background thread.
cordova.getThreadPool().execute(new Runnable() {
public void run() {
synchronized (context) {
File file = context.targetFile;
if (file != null) {
file.delete();
}
// Trigger the abort callback immediately to minimize latency between it and abort() being called.
JSONObject error = createFileTransferError(ABORTED_ERR, context.source, context.target, null, -1, null);
context.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, error));
context.aborted = true;
if (context.connection != null) {
try {
context.connection.disconnect();
} catch (Exception e) {
LOG.e(LOG_TAG, "CB-8431 Catch workaround for fatal exception", e);
}
}
}
}
});
}
}
}

View File

@@ -0,0 +1,73 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova.filetransfer;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Encapsulates the result and/or status of uploading a file to a remote server.
*/
public class FileUploadResult {
private long bytesSent = 0; // bytes sent
private int responseCode = -1; // HTTP response code
private String response = null; // HTTP response
private String objectId = null; // FileTransfer object id
public long getBytesSent() {
return bytesSent;
}
public void setBytesSent(long bytes) {
this.bytesSent = bytes;
}
public int getResponseCode() {
return responseCode;
}
public void setResponseCode(int responseCode) {
this.responseCode = responseCode;
}
public String getResponse() {
return response;
}
public void setResponse(String response) {
this.response = response;
}
public String getObjectId() {
return objectId;
}
public void setObjectId(String objectId) {
this.objectId = objectId;
}
public JSONObject toJSONObject() throws JSONException {
return new JSONObject(
"{bytesSent:" + bytesSent +
",responseCode:" + responseCode +
",response:" + JSONObject.quote(response) +
",objectId:" + JSONObject.quote(objectId) + "}");
}
}

View File

@@ -0,0 +1,89 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
#import <Cordova/CDVPlugin.h>
#import "CDVFile.h"
enum CDVFileTransferError {
FILE_NOT_FOUND_ERR = 1,
INVALID_URL_ERR = 2,
CONNECTION_ERR = 3,
CONNECTION_ABORTED = 4,
NOT_MODIFIED = 5
};
typedef int CDVFileTransferError;
enum CDVFileTransferDirection {
CDV_TRANSFER_UPLOAD = 1,
CDV_TRANSFER_DOWNLOAD = 2,
};
typedef int CDVFileTransferDirection;
// Magic value within the options dict used to set a cookie.
extern NSString* const kOptionsKeyCookie;
@interface CDVFileTransfer : CDVPlugin {}
- (void)upload:(CDVInvokedUrlCommand*)command;
- (void)download:(CDVInvokedUrlCommand*)command;
- (NSString*)escapePathComponentForUrlString:(NSString*)urlString;
// Visible for testing.
- (NSURLRequest*)requestForUploadCommand:(CDVInvokedUrlCommand*)command fileData:(NSData*)fileData;
- (NSMutableDictionary*)createFileTransferError:(int)code AndSource:(NSString*)source AndTarget:(NSString*)target;
- (NSMutableDictionary*)createFileTransferError:(int)code
AndSource:(NSString*)source
AndTarget:(NSString*)target
AndHttpStatus:(int)httpStatus
AndBody:(NSString*)body;
@property (nonatomic, strong) NSOperationQueue* queue;
@property (readonly) NSMutableDictionary* activeTransfers;
@end
@class CDVFileTransferEntityLengthRequest;
@interface CDVFileTransferDelegate : NSObject {}
- (void)updateBytesExpected:(long long)newBytesExpected;
- (void)cancelTransfer:(NSURLConnection*)connection;
@property (strong) NSMutableData* responseData; // atomic
@property (nonatomic, strong) NSDictionary* responseHeaders;
@property (nonatomic, assign) UIBackgroundTaskIdentifier backgroundTaskID;
@property (nonatomic, strong) CDVFileTransfer* command;
@property (nonatomic, assign) CDVFileTransferDirection direction;
@property (nonatomic, strong) NSURLConnection* connection;
@property (nonatomic, copy) NSString* callbackId;
@property (nonatomic, copy) NSString* objectId;
@property (nonatomic, copy) NSString* source;
@property (nonatomic, copy) NSString* target;
@property (nonatomic, copy) NSURL* targetURL;
@property (nonatomic, copy) NSString* mimeType;
@property (assign) int responseCode; // atomic
@property (nonatomic, assign) long long bytesTransfered;
@property (nonatomic, assign) long long bytesExpected;
@property (nonatomic, assign) BOOL trustAllHosts;
@property (strong) NSFileHandle* targetFileHandle;
@property (nonatomic, strong) CDVFileTransferEntityLengthRequest* entityLengthRequest;
@property (nonatomic, strong) CDVFile *filePlugin;
@property (nonatomic, assign) BOOL chunkedMode;
@end

View File

@@ -0,0 +1,861 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Cordova/CDV.h>
#import "CDVFileTransfer.h"
#import "CDVLocalFilesystem.h"
#import <AssetsLibrary/ALAsset.h>
#import <AssetsLibrary/ALAssetRepresentation.h>
#import <AssetsLibrary/ALAssetsLibrary.h>
#import <CFNetwork/CFNetwork.h>
#ifndef DLog
#ifdef DEBUG
#define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define DLog(...)
#endif
#endif
@interface CDVFileTransfer ()
// Sets the requests headers for the request.
- (void)applyRequestHeaders:(NSDictionary*)headers toRequest:(NSMutableURLRequest*)req;
// Creates a delegate to handle an upload.
- (CDVFileTransferDelegate*)delegateForUploadCommand:(CDVInvokedUrlCommand*)command;
// Creates an NSData* for the file for the given upload arguments.
- (void)fileDataForUploadCommand:(CDVInvokedUrlCommand*)command;
@end
// Buffer size to use for streaming uploads.
static const NSUInteger kStreamBufferSize = 32768;
// Magic value within the options dict used to set a cookie.
NSString* const kOptionsKeyCookie = @"__cookie";
// Form boundary for multi-part requests.
NSString* const kFormBoundary = @"+++++org.apache.cordova.formBoundary";
// Writes the given data to the stream in a blocking way.
// If successful, returns bytesToWrite.
// If the stream was closed on the other end, returns 0.
// If there was an error, returns -1.
static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
{
UInt8* bytes = (UInt8*)[data bytes];
long long bytesToWrite = [data length];
long long totalBytesWritten = 0;
while (totalBytesWritten < bytesToWrite) {
CFIndex result = CFWriteStreamWrite(stream,
bytes + totalBytesWritten,
bytesToWrite - totalBytesWritten);
if (result < 0) {
CFStreamError error = CFWriteStreamGetError(stream);
NSLog(@"WriteStreamError domain: %ld error: %ld", error.domain, (long)error.error);
return result;
} else if (result == 0) {
return result;
}
totalBytesWritten += result;
}
return totalBytesWritten;
}
@implementation CDVFileTransfer
@synthesize activeTransfers;
- (void)pluginInitialize {
activeTransfers = [[NSMutableDictionary alloc] init];
}
- (NSString*)escapePathComponentForUrlString:(NSString*)urlString
{
NSRange schemeAndHostRange = [urlString rangeOfString:@"://.*?/" options:NSRegularExpressionSearch];
if (schemeAndHostRange.length == 0) {
return urlString;
}
NSInteger schemeAndHostEndIndex = NSMaxRange(schemeAndHostRange);
NSString* schemeAndHost = [urlString substringToIndex:schemeAndHostEndIndex];
NSString* pathComponent = [urlString substringFromIndex:schemeAndHostEndIndex];
pathComponent = [pathComponent stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
return [schemeAndHost stringByAppendingString:pathComponent];
}
- (void)applyRequestHeaders:(NSDictionary*)headers toRequest:(NSMutableURLRequest*)req
{
[req setValue:@"XMLHttpRequest" forHTTPHeaderField:@"X-Requested-With"];
NSString* userAgent = [self.commandDelegate userAgent];
if (userAgent) {
[req setValue:userAgent forHTTPHeaderField:@"User-Agent"];
}
for (NSString* headerName in headers) {
id value = [headers objectForKey:headerName];
if (!value || (value == [NSNull null])) {
value = @"null";
}
// First, remove an existing header if one exists.
[req setValue:nil forHTTPHeaderField:headerName];
if (![value isKindOfClass:[NSArray class]]) {
value = [NSArray arrayWithObject:value];
}
// Then, append all header values.
for (id __strong subValue in value) {
// Convert from an NSNumber -> NSString.
if ([subValue respondsToSelector:@selector(stringValue)]) {
subValue = [subValue stringValue];
}
if ([subValue isKindOfClass:[NSString class]]) {
[req addValue:subValue forHTTPHeaderField:headerName];
}
}
}
}
- (NSURLRequest*)requestForUploadCommand:(CDVInvokedUrlCommand*)command fileData:(NSData*)fileData
{
// arguments order from js: [filePath, server, fileKey, fileName, mimeType, params, debug, chunkedMode]
// however, params is a JavaScript object and during marshalling is put into the options dict,
// thus debug and chunkedMode are the 6th and 7th arguments
NSString* target = [command argumentAtIndex:0];
NSString* server = [command argumentAtIndex:1];
NSString* fileKey = [command argumentAtIndex:2 withDefault:@"file"];
NSString* fileName = [command argumentAtIndex:3 withDefault:@"image.jpg"];
NSString* mimeType = [command argumentAtIndex:4 withDefault:@"image/jpeg"];
NSDictionary* options = [command argumentAtIndex:5 withDefault:nil];
// BOOL trustAllHosts = [[command argumentAtIndex:6 withDefault:[NSNumber numberWithBool:YES]] boolValue]; // allow self-signed certs
BOOL chunkedMode = [[command argumentAtIndex:7 withDefault:[NSNumber numberWithBool:YES]] boolValue];
NSDictionary* headers = [command argumentAtIndex:8 withDefault:nil];
// Allow alternative http method, default to POST. JS side checks
// for allowed methods, currently PUT or POST (forces POST for
// unrecognised values)
NSString* httpMethod = [command argumentAtIndex:10 withDefault:@"POST"];
CDVPluginResult* result = nil;
CDVFileTransferError errorCode = 0;
// NSURL does not accepts URLs with spaces in the path. We escape the path in order
// to be more lenient.
NSURL* url = [NSURL URLWithString:server];
if (!url) {
errorCode = INVALID_URL_ERR;
NSLog(@"File Transfer Error: Invalid server URL %@", server);
} else if (!fileData) {
errorCode = FILE_NOT_FOUND_ERR;
}
if (errorCode > 0) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createFileTransferError:errorCode AndSource:target AndTarget:server]];
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
return nil;
}
NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:url];
[req setHTTPMethod:httpMethod];
// Magic value to set a cookie
if ([options objectForKey:kOptionsKeyCookie]) {
[req setValue:[options objectForKey:kOptionsKeyCookie] forHTTPHeaderField:@"Cookie"];
[req setHTTPShouldHandleCookies:NO];
}
// if we specified a Content-Type header, don't do multipart form upload
BOOL multipartFormUpload = [headers objectForKey:@"Content-Type"] == nil;
if (multipartFormUpload) {
NSString* contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", kFormBoundary];
[req setValue:contentType forHTTPHeaderField:@"Content-Type"];
}
[self applyRequestHeaders:headers toRequest:req];
NSData* formBoundaryData = [[NSString stringWithFormat:@"--%@\r\n", kFormBoundary] dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData* postBodyBeforeFile = [NSMutableData data];
for (NSString* key in options) {
id val = [options objectForKey:key];
if (!val || (val == [NSNull null]) || [key isEqualToString:kOptionsKeyCookie]) {
continue;
}
// if it responds to stringValue selector (eg NSNumber) get the NSString
if ([val respondsToSelector:@selector(stringValue)]) {
val = [val stringValue];
}
// finally, check whether it is a NSString (for dataUsingEncoding selector below)
if (![val isKindOfClass:[NSString class]]) {
continue;
}
[postBodyBeforeFile appendData:formBoundaryData];
[postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
[postBodyBeforeFile appendData:[val dataUsingEncoding:NSUTF8StringEncoding]];
[postBodyBeforeFile appendData:[@"\r\n" dataUsingEncoding : NSUTF8StringEncoding]];
}
[postBodyBeforeFile appendData:formBoundaryData];
[postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", fileKey, fileName] dataUsingEncoding:NSUTF8StringEncoding]];
if (mimeType != nil) {
[postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n", mimeType] dataUsingEncoding:NSUTF8StringEncoding]];
}
[postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Length: %ld\r\n\r\n", (long)[fileData length]] dataUsingEncoding:NSUTF8StringEncoding]];
DLog(@"fileData length: %ld", [fileData length]);
NSData* postBodyAfterFile = [[NSString stringWithFormat:@"\r\n--%@--\r\n", kFormBoundary] dataUsingEncoding:NSUTF8StringEncoding];
long long totalPayloadLength = [fileData length];
if (multipartFormUpload) {
totalPayloadLength += [postBodyBeforeFile length] + [postBodyAfterFile length];
}
[req setValue:[[NSNumber numberWithLongLong:totalPayloadLength] stringValue] forHTTPHeaderField:@"Content-Length"];
if (chunkedMode) {
CFReadStreamRef readStream = NULL;
CFWriteStreamRef writeStream = NULL;
CFStreamCreateBoundPair(NULL, &readStream, &writeStream, kStreamBufferSize);
[req setHTTPBodyStream:CFBridgingRelease(readStream)];
[self.commandDelegate runInBackground:^{
if (CFWriteStreamOpen(writeStream)) {
if (multipartFormUpload) {
NSData* chunks[] = { postBodyBeforeFile, fileData, postBodyAfterFile };
int numChunks = sizeof(chunks) / sizeof(chunks[0]);
for (int i = 0; i < numChunks; ++i) {
// Allow uploading of an empty file
if (chunks[i].length == 0) {
continue;
}
CFIndex result = WriteDataToStream(chunks[i], writeStream);
if (result <= 0) {
break;
}
}
} else {
if (totalPayloadLength > 0) {
WriteDataToStream(fileData, writeStream);
} else {
NSLog(@"Uploading of an empty file is not supported for chunkedMode=true and multipart=false");
}
}
} else {
NSLog(@"FileTransfer: Failed to open writeStream");
}
CFWriteStreamClose(writeStream);
CFRelease(writeStream);
}];
} else {
if (multipartFormUpload) {
[postBodyBeforeFile appendData:fileData];
[postBodyBeforeFile appendData:postBodyAfterFile];
[req setHTTPBody:postBodyBeforeFile];
} else {
[req setHTTPBody:fileData];
}
}
return req;
}
- (CDVFileTransferDelegate*)delegateForUploadCommand:(CDVInvokedUrlCommand*)command
{
NSString* source = [command argumentAtIndex:0];
NSString* server = [command argumentAtIndex:1];
BOOL trustAllHosts = [[command argumentAtIndex:6 withDefault:[NSNumber numberWithBool:NO]] boolValue]; // allow self-signed certs
NSString* objectId = [command argumentAtIndex:9];
BOOL chunkedMode = [[command argumentAtIndex:7 withDefault:[NSNumber numberWithBool:YES]] boolValue];
CDVFileTransferDelegate* delegate = [[CDVFileTransferDelegate alloc] init];
delegate.command = self;
delegate.callbackId = command.callbackId;
delegate.direction = CDV_TRANSFER_UPLOAD;
delegate.objectId = objectId;
delegate.source = source;
delegate.target = server;
delegate.trustAllHosts = trustAllHosts;
delegate.filePlugin = [self.commandDelegate getCommandInstance:@"File"];
delegate.chunkedMode = chunkedMode;
return delegate;
}
- (void)fileDataForUploadCommand:(CDVInvokedUrlCommand*)command
{
NSString* source = (NSString*)[command argumentAtIndex:0];
NSString* server = [command argumentAtIndex:1];
NSError* __autoreleasing err = nil;
if ([source hasPrefix:@"data:"] && [source rangeOfString:@"base64"].location != NSNotFound) {
NSRange commaRange = [source rangeOfString: @","];
if (commaRange.location == NSNotFound) {
// Return error is there is no comma
__weak CDVFileTransfer* weakSelf = self;
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[weakSelf createFileTransferError:INVALID_URL_ERR AndSource:source AndTarget:server]];
[weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId];
return;
}
if (commaRange.location + 1 > source.length - 1) {
// Init as an empty data
NSData *fileData = [[NSData alloc] init];
[self uploadData:fileData command:command];
return;
}
NSData *fileData = [[NSData alloc] initWithBase64EncodedString:[source substringFromIndex:(commaRange.location + 1)] options:NSDataBase64DecodingIgnoreUnknownCharacters];
[self uploadData:fileData command:command];
return;
}
CDVFilesystemURL *sourceURL = [CDVFilesystemURL fileSystemURLWithString:source];
NSObject<CDVFileSystem> *fs;
if (sourceURL) {
// Try to get a CDVFileSystem which will handle this file.
// This requires talking to the current CDVFile plugin.
fs = [[self.commandDelegate getCommandInstance:@"File"] filesystemForURL:sourceURL];
}
if (fs) {
__weak CDVFileTransfer* weakSelf = self;
[fs readFileAtURL:sourceURL start:0 end:-1 callback:^(NSData *fileData, NSString *mimeType, CDVFileError err) {
if (err) {
// We couldn't find the asset. Send the appropriate error.
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[weakSelf createFileTransferError:NOT_FOUND_ERR AndSource:source AndTarget:server]];
[weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId];
} else {
[weakSelf uploadData:fileData command:command];
}
}];
return;
} else {
// Extract the path part out of a file: URL.
NSString* filePath = [source hasPrefix:@"/"] ? [source copy] : [(NSURL *)[NSURL URLWithString:source] path];
if (filePath == nil) {
// We couldn't find the asset. Send the appropriate error.
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createFileTransferError:NOT_FOUND_ERR AndSource:source AndTarget:server]];
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
return;
}
// Memory map the file so that it can be read efficiently even if it is large.
NSData* fileData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&err];
if (err != nil) {
NSLog(@"Error opening file %@: %@", source, err);
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createFileTransferError:NOT_FOUND_ERR AndSource:source AndTarget:server]];
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
} else {
[self uploadData:fileData command:command];
}
}
}
- (void)upload:(CDVInvokedUrlCommand*)command
{
// fileData and req are split into helper functions to ease the unit testing of delegateForUpload.
// First, get the file data. This method will call `uploadData:command`.
[self fileDataForUploadCommand:command];
}
- (void)uploadData:(NSData*)fileData command:(CDVInvokedUrlCommand*)command
{
NSURLRequest* req = [self requestForUploadCommand:command fileData:fileData];
if (req == nil) {
return;
}
CDVFileTransferDelegate* delegate = [self delegateForUploadCommand:command];
delegate.connection = [[NSURLConnection alloc] initWithRequest:req delegate:delegate startImmediately:NO];
if (self.queue == nil) {
self.queue = [[NSOperationQueue alloc] init];
}
[delegate.connection setDelegateQueue:self.queue];
// sets a background task ID for the transfer object.
delegate.backgroundTaskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[delegate cancelTransfer:delegate.connection];
}];
@synchronized (activeTransfers) {
activeTransfers[delegate.objectId] = delegate;
}
[delegate.connection start];
}
- (void)abort:(CDVInvokedUrlCommand*)command
{
NSString* objectId = [command argumentAtIndex:0];
@synchronized (activeTransfers) {
CDVFileTransferDelegate* delegate = activeTransfers[objectId];
if (delegate != nil) {
[delegate cancelTransfer:delegate.connection];
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createFileTransferError:CONNECTION_ABORTED AndSource:delegate.source AndTarget:delegate.target]];
[self.commandDelegate sendPluginResult:result callbackId:delegate.callbackId];
}
}
}
- (void)download:(CDVInvokedUrlCommand*)command
{
DLog(@"File Transfer downloading file...");
NSString* source = [command argumentAtIndex:0];
NSString* target = [command argumentAtIndex:1];
BOOL trustAllHosts = [[command argumentAtIndex:2 withDefault:[NSNumber numberWithBool:NO]] boolValue]; // allow self-signed certs
NSString* objectId = [command argumentAtIndex:3];
NSDictionary* headers = [command argumentAtIndex:4 withDefault:nil];
CDVPluginResult* result = nil;
CDVFileTransferError errorCode = 0;
NSURL* targetURL;
if ([target hasPrefix:@"/"]) {
/* Backwards-compatibility:
* Check here to see if it looks like the user passed in a raw filesystem path. (Perhaps they had the path saved, and were previously using it with the old version of File). If so, normalize it by removing empty path segments, and check with File to see if any of the installed filesystems will handle it. If so, then we will end up with a filesystem url to use for the remainder of this operation.
*/
target = [target stringByReplacingOccurrencesOfString:@"//" withString:@"/"];
targetURL = [[self.commandDelegate getCommandInstance:@"File"] fileSystemURLforLocalPath:target].url;
} else {
targetURL = [NSURL URLWithString:target];
if (targetURL == nil) {
NSString* targetUrlTextEscaped = [target stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];
if (targetUrlTextEscaped) {
targetURL = [NSURL URLWithString:targetUrlTextEscaped];
}
}
}
NSURL* sourceURL = [NSURL URLWithString:source];
if (!sourceURL) {
errorCode = INVALID_URL_ERR;
NSLog(@"File Transfer Error: Invalid server URL %@", source);
} else if (!targetURL) {
errorCode = INVALID_URL_ERR;
NSLog(@"File Tranfer Error: Invalid target URL %@", target);
} else if (![targetURL isFileURL]) {
CDVFilesystemURL *fsURL = [CDVFilesystemURL fileSystemURLWithString:target];
if (!fsURL) {
errorCode = FILE_NOT_FOUND_ERR;
NSLog(@"File Transfer Error: Invalid file path or URL %@", target);
}
}
if (errorCode > 0) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createFileTransferError:errorCode AndSource:source AndTarget:target]];
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
return;
}
NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:sourceURL];
[self applyRequestHeaders:headers toRequest:req];
CDVFileTransferDelegate* delegate = [[CDVFileTransferDelegate alloc] init];
delegate.command = self;
delegate.direction = CDV_TRANSFER_DOWNLOAD;
delegate.callbackId = command.callbackId;
delegate.objectId = objectId;
delegate.source = source;
delegate.target = [targetURL absoluteString];
delegate.targetURL = targetURL;
delegate.trustAllHosts = trustAllHosts;
delegate.filePlugin = [self.commandDelegate getCommandInstance:@"File"];
delegate.backgroundTaskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[delegate cancelTransfer:delegate.connection];
}];
delegate.connection = [[NSURLConnection alloc] initWithRequest:req delegate:delegate startImmediately:NO];
if (self.queue == nil) {
self.queue = [[NSOperationQueue alloc] init];
}
[delegate.connection setDelegateQueue:self.queue];
@synchronized (activeTransfers) {
activeTransfers[delegate.objectId] = delegate;
}
// Downloads can take time
// sending this to a new thread calling the download_async method
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL),
^(void) { [delegate.connection start];}
);
}
- (NSMutableDictionary*)createFileTransferError:(int)code AndSource:(NSString*)source AndTarget:(NSString*)target
{
NSMutableDictionary* result = [NSMutableDictionary dictionaryWithCapacity:3];
[result setObject:[NSNumber numberWithInt:code] forKey:@"code"];
if (source != nil) {
[result setObject:source forKey:@"source"];
}
if (target != nil) {
[result setObject:target forKey:@"target"];
}
NSLog(@"FileTransferError %@", result);
return result;
}
- (NSMutableDictionary*)createFileTransferError:(int)code
AndSource:(NSString*)source
AndTarget:(NSString*)target
AndHttpStatus:(int)httpStatus
AndBody:(NSString*)body
{
NSMutableDictionary* result = [NSMutableDictionary dictionaryWithCapacity:5];
[result setObject:[NSNumber numberWithInt:code] forKey:@"code"];
if (source != nil) {
[result setObject:source forKey:@"source"];
}
if (target != nil) {
[result setObject:target forKey:@"target"];
}
[result setObject:[NSNumber numberWithInt:httpStatus] forKey:@"http_status"];
if (body != nil) {
[result setObject:body forKey:@"body"];
}
NSLog(@"FileTransferError %@", result);
return result;
}
- (void)onReset {
@synchronized (activeTransfers) {
while ([activeTransfers count] > 0) {
CDVFileTransferDelegate* delegate = [activeTransfers allValues][0];
[delegate cancelTransfer:delegate.connection];
}
}
}
@end
@interface CDVFileTransferEntityLengthRequest : NSObject {
NSURLConnection* _connection;
CDVFileTransferDelegate* __weak _originalDelegate;
}
- (CDVFileTransferEntityLengthRequest*)initWithOriginalRequest:(NSURLRequest*)originalRequest andDelegate:(CDVFileTransferDelegate*)originalDelegate;
@end
@implementation CDVFileTransferEntityLengthRequest
- (CDVFileTransferEntityLengthRequest*)initWithOriginalRequest:(NSURLRequest*)originalRequest andDelegate:(CDVFileTransferDelegate*)originalDelegate
{
if (self) {
DLog(@"Requesting entity length for GZIPped content...");
NSMutableURLRequest* req = [originalRequest mutableCopy];
[req setHTTPMethod:@"HEAD"];
[req setValue:@"identity" forHTTPHeaderField:@"Accept-Encoding"];
_originalDelegate = originalDelegate;
_connection = [NSURLConnection connectionWithRequest:req delegate:self];
}
return self;
}
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
{
DLog(@"HEAD request returned; content-length is %lld", [response expectedContentLength]);
[_originalDelegate updateBytesExpected:[response expectedContentLength]];
}
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{}
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{}
@end
@implementation CDVFileTransferDelegate
@synthesize callbackId, connection = _connection, source, target, responseData, responseHeaders, command, bytesTransfered, bytesExpected, direction, responseCode, objectId, targetFileHandle, filePlugin;
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
NSString* uploadResponse = nil;
NSString* downloadResponse = nil;
NSMutableDictionary* uploadResult;
CDVPluginResult* result = nil;
NSLog(@"File Transfer Finished with response code %d", self.responseCode);
if (self.direction == CDV_TRANSFER_UPLOAD) {
uploadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
if (uploadResponse == nil) {
uploadResponse = [[NSString alloc] initWithData: self.responseData encoding:NSISOLatin1StringEncoding];
}
if ((self.responseCode >= 200) && (self.responseCode < 300)) {
// create dictionary to return FileUploadResult object
uploadResult = [NSMutableDictionary dictionaryWithCapacity:3];
if (uploadResponse != nil) {
[uploadResult setObject:uploadResponse forKey:@"response"];
[uploadResult setObject:self.responseHeaders forKey:@"headers"];
}
[uploadResult setObject:[NSNumber numberWithLongLong:self.bytesTransfered] forKey:@"bytesSent"];
[uploadResult setObject:[NSNumber numberWithInt:self.responseCode] forKey:@"responseCode"];
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:uploadResult];
} else {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:uploadResponse]];
}
}
if (self.direction == CDV_TRANSFER_DOWNLOAD) {
if (self.targetFileHandle) {
[self.targetFileHandle closeFile];
self.targetFileHandle = nil;
DLog(@"File Transfer Download success");
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self.filePlugin makeEntryForURL:self.targetURL]];
} else {
downloadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
if (downloadResponse == nil) {
downloadResponse = [[NSString alloc] initWithData: self.responseData encoding:NSISOLatin1StringEncoding];
}
CDVFileTransferError errorCode = self.responseCode == 404 ? FILE_NOT_FOUND_ERR
: (self.responseCode == 304 ? NOT_MODIFIED : CONNECTION_ERR);
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:errorCode AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:downloadResponse]];
}
}
[self.command.commandDelegate sendPluginResult:result callbackId:callbackId];
// remove connection for activeTransfers
@synchronized (command.activeTransfers) {
[command.activeTransfers removeObjectForKey:objectId];
// remove background id task in case our upload was done in the background
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskID];
self.backgroundTaskID = UIBackgroundTaskInvalid;
}
}
- (void)removeTargetFile
{
NSFileManager* fileMgr = [NSFileManager defaultManager];
NSString *targetPath = [self targetFilePath];
if ([fileMgr fileExistsAtPath:targetPath])
{
[fileMgr removeItemAtPath:targetPath error:nil];
}
}
- (void)cancelTransfer:(NSURLConnection*)connection
{
[connection cancel];
@synchronized (self.command.activeTransfers) {
CDVFileTransferDelegate* delegate = self.command.activeTransfers[self.objectId];
[self.command.activeTransfers removeObjectForKey:self.objectId];
[[UIApplication sharedApplication] endBackgroundTask:delegate.backgroundTaskID];
delegate.backgroundTaskID = UIBackgroundTaskInvalid;
}
if (self.direction == CDV_TRANSFER_DOWNLOAD) {
[self removeTargetFile];
}
}
- (void)cancelTransferWithError:(NSURLConnection*)connection errorMessage:(NSString*)errorMessage
{
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsDictionary:[self.command createFileTransferError:FILE_NOT_FOUND_ERR AndSource:self.source AndTarget:self.target AndHttpStatus:self.responseCode AndBody:errorMessage]];
NSLog(@"File Transfer Error: %@", errorMessage);
[self cancelTransfer:connection];
[self.command.commandDelegate sendPluginResult:result callbackId:callbackId];
}
- (NSString *)targetFilePath
{
NSString *path = nil;
CDVFilesystemURL *sourceURL = [CDVFilesystemURL fileSystemURLWithString:self.target];
if (sourceURL && sourceURL.fileSystemName != nil) {
// This requires talking to the current CDVFile plugin
NSObject<CDVFileSystem> *fs = [self.filePlugin filesystemForURL:sourceURL];
path = [fs filesystemPathForURL:sourceURL];
} else {
// Extract the path part out of a file: URL.
path = [self.target hasPrefix:@"/"] ? [self.target copy] : [(NSURL *)[NSURL URLWithString:self.target] path];
}
return path;
}
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
{
NSError* __autoreleasing error = nil;
self.mimeType = [response MIMEType];
self.targetFileHandle = nil;
// required for iOS 4.3, for some reason; response is
// a plain NSURLResponse, not the HTTP subclass
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
self.responseCode = (int)[httpResponse statusCode];
self.bytesExpected = [response expectedContentLength];
self.responseHeaders = [httpResponse allHeaderFields];
if ((self.direction == CDV_TRANSFER_DOWNLOAD) && (self.responseCode == 200) && (self.bytesExpected == NSURLResponseUnknownLength)) {
// Kick off HEAD request to server to get real length
// bytesExpected will be updated when that response is returned
self.entityLengthRequest = [[CDVFileTransferEntityLengthRequest alloc] initWithOriginalRequest:connection.currentRequest andDelegate:self];
}
} else if ([response.URL isFileURL]) {
NSDictionary* attr = [[NSFileManager defaultManager] attributesOfItemAtPath:[response.URL path] error:nil];
self.responseCode = 200;
self.bytesExpected = [attr[NSFileSize] longLongValue];
} else {
self.responseCode = 200;
self.bytesExpected = NSURLResponseUnknownLength;
}
if ((self.direction == CDV_TRANSFER_DOWNLOAD) && (self.responseCode >= 200) && (self.responseCode < 300)) {
// Download response is okay; begin streaming output to file
NSString *filePath = [self targetFilePath];
if (filePath == nil) {
// We couldn't find the asset. Send the appropriate error.
[self cancelTransferWithError:connection errorMessage:[NSString stringWithFormat:@"Could not create target file"]];
return;
}
NSString* parentPath = [filePath stringByDeletingLastPathComponent];
// create parent directories if needed
if ([[NSFileManager defaultManager] createDirectoryAtPath:parentPath withIntermediateDirectories:YES attributes:nil error:&error] == NO) {
if (error) {
[self cancelTransferWithError:connection errorMessage:[NSString stringWithFormat:@"Could not create path to save downloaded file: %@", [error localizedDescription]]];
} else {
[self cancelTransferWithError:connection errorMessage:@"Could not create path to save downloaded file"];
}
return;
}
// create target file
if ([[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil] == NO) {
[self cancelTransferWithError:connection errorMessage:@"Could not create target file"];
return;
}
// open target file for writing
self.targetFileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath];
if (self.targetFileHandle == nil) {
[self cancelTransferWithError:connection errorMessage:@"Could not open target file for writing"];
}
DLog(@"Streaming to file %@", filePath);
}
}
- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error
{
NSString* body = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:body]];
NSLog(@"File Transfer Error: %@", [error localizedDescription]);
[self cancelTransfer:connection];
[self.command.commandDelegate sendPluginResult:result callbackId:callbackId];
}
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
self.bytesTransfered += data.length;
if (self.targetFileHandle) {
[self.targetFileHandle writeData:data];
} else {
[self.responseData appendData:data];
}
[self updateProgress];
}
- (void)updateBytesExpected:(long long)newBytesExpected
{
DLog(@"Updating bytesExpected to %lld", newBytesExpected);
self.bytesExpected = newBytesExpected;
[self updateProgress];
}
- (void)updateProgress
{
if (self.direction == CDV_TRANSFER_DOWNLOAD) {
BOOL lengthComputable = (self.bytesExpected != NSURLResponseUnknownLength);
// If the response is GZipped, and we have an outstanding HEAD request to get
// the length, then hold off on sending progress events.
if (!lengthComputable && (self.entityLengthRequest != nil)) {
return;
}
NSMutableDictionary* downloadProgress = [NSMutableDictionary dictionaryWithCapacity:3];
[downloadProgress setObject:[NSNumber numberWithBool:lengthComputable] forKey:@"lengthComputable"];
[downloadProgress setObject:[NSNumber numberWithLongLong:self.bytesTransfered] forKey:@"loaded"];
[downloadProgress setObject:[NSNumber numberWithLongLong:self.bytesExpected] forKey:@"total"];
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:downloadProgress];
[result setKeepCallbackAsBool:true];
[self.command.commandDelegate sendPluginResult:result callbackId:callbackId];
}
}
- (void)connection:(NSURLConnection*)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
{
if (self.direction == CDV_TRANSFER_UPLOAD) {
NSMutableDictionary* uploadProgress = [NSMutableDictionary dictionaryWithCapacity:3];
[uploadProgress setObject:[NSNumber numberWithBool:true] forKey:@"lengthComputable"];
[uploadProgress setObject:[NSNumber numberWithLongLong:totalBytesWritten] forKey:@"loaded"];
[uploadProgress setObject:[NSNumber numberWithLongLong:totalBytesExpectedToWrite] forKey:@"total"];
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:uploadProgress];
[result setKeepCallbackAsBool:true];
[self.command.commandDelegate sendPluginResult:result callbackId:callbackId];
}
self.bytesTransfered = totalBytesWritten;
}
// for self signed certificates
- (void)connection:(NSURLConnection*)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge
{
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if (self.trustAllHosts) {
NSURLCredential* credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
}
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
} else {
[challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge];
}
}
- (id)init
{
if ((self = [super init])) {
self.responseData = [NSMutableData data];
self.targetFileHandle = nil;
}
return self;
}
@end

View File

@@ -0,0 +1,265 @@
/*
*
* Copyright 2013 Canonical Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
#include "file-transfer.h"
#include <plugins/cordova-plugin-file/file.h>
#include <cassert>
static void SetHeaders(QNetworkRequest &request, const QVariantMap &headers) {
for (const QString &key: headers.keys()) {
QVariant val = *headers.find(key);
QString value = val.toString();
if (val.userType() == QMetaType::QVariantList || val.userType() == QMetaType::QStringList) {
QList<QVariant> list = val.toList();
for (QVariant v: list) {
if (value.size())
value += ", ";
value += v.toString();
}
}
request.setRawHeader(key.toUtf8(), value.toUtf8());
}
}
void FileTransfer::download(int scId, int ecId, const QString& url, const QString &target, bool /*trustAllHost*/, int id, const QVariantMap &headers) {
QSharedPointer<FileTransferRequest> request(new FileTransferRequest(_manager, scId, ecId, id, this));
assert(_id2request.find(id) == _id2request.end());
_id2request.insert(id, request);
request->connect(request.data(), &FileTransferRequest::done, [&]() {
auto it = _id2request.find(id);
while (it != _id2request.end() && it.key() == id) {
if (it.value().data() == request.data()) {
_id2request.erase(it);
break;
}
it++;
}
});
request->download(url, target, headers);
}
void FileTransfer::upload(int scId, int ecId, const QString &fileURI, const QString& url, const QString& fileKey, const QString& fileName, const QString& mimeType,
const QVariantMap & params, bool /*trustAllHosts*/, bool /*chunkedMode*/, const QVariantMap &headers, int id, const QString &/*httpMethod*/) {
QSharedPointer<FileTransferRequest> request(new FileTransferRequest(_manager, scId, ecId, id, this));
assert(_id2request.find(id) == _id2request.end());
_id2request.insert(id, request);
request->connect(request.data(), &FileTransferRequest::done, [&]() {
auto it = _id2request.find(id);
while (it != _id2request.end() && it.key() == id) {
if (it.value().data() == request.data()) {
_id2request.erase(it);
break;
}
it++;
}
});
request->upload(url, fileURI, fileKey, fileName, mimeType, params, headers);
}
void FileTransfer::abort(int scId, int ecId, int id) {
Q_UNUSED(scId)
Q_UNUSED(ecId)
auto it = _id2request.find(id);
while (it != _id2request.end() && it.key() == id) {
(*it)->abort();
it++;
}
}
void FileTransferRequest::download(const QString& uri, const QString &targetURI, const QVariantMap &headers) {
QUrl url(uri);
QNetworkRequest request;
QSharedPointer<CPlugin> filePlugin(_plugin->cordova()->getPlugin<File>());
if (!filePlugin.data())
return;
if (!url.isValid()) {
QVariantMap map;
map.insert("code", INVALID_URL_ERR);
map.insert("source", uri);
map.insert("target", targetURI);
_plugin->cb(_ecId, map);
emit done();
return;
}
request.setUrl(url);
if (url.password().size() || url.userName().size()) {
QString headerData = "Basic " + (url.userName() + ":" + url.password()).toLocal8Bit().toBase64();
request.setRawHeader("Authorization", headerData.toLocal8Bit());
}
SetHeaders(request, headers);
_reply = QSharedPointer<QNetworkReply>(_manager.get(request));
_reply->connect(_reply.data(), &QNetworkReply::finished, [this, targetURI, uri, filePlugin]() {
if (!_scId || _reply->error() != QNetworkReply::NoError)
return;
QPair<bool, QFileInfo> f1(dynamic_cast<File*>(filePlugin.data())->resolveURI(targetURI));
QFile res(f1.second.absoluteFilePath());
if (!f1.first || !res.open(QIODevice::WriteOnly)) {
QVariantMap map;
map.insert("code", INVALID_URL_ERR);
map.insert("source", uri);
map.insert("target", targetURI);
_plugin->cb(_ecId, map);
emit done();
return;
}
res.write(_reply->readAll());
_plugin->cb(_scId, dynamic_cast<File*>(filePlugin.data())->file2map(f1.second));
emit done();
});
_reply->connect(_reply.data(), SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(error(QNetworkReply::NetworkError)));
_reply->connect(_reply.data(), SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(progress(qint64, qint64)));
}
void FileTransferRequest::upload(const QString& _url, const QString& fileURI, QString fileKey, QString fileName, QString mimeType, const QVariantMap &params, const QVariantMap &headers) {
QUrl url(_url);
QNetworkRequest request;
QSharedPointer<CPlugin> filePlugin(_plugin->cordova()->getPlugin<File>());
if (!filePlugin.data())
return;
if (!url.isValid()) {
QVariantMap map;
map.insert("code", INVALID_URL_ERR);
map.insert("source", fileURI);
map.insert("target", _url);
_plugin->cb(_ecId, map);
emit done();
return;
}
QPair<bool, QFileInfo> f1(dynamic_cast<File*>(filePlugin.data())->resolveURI(fileURI));
QFile file(f1.second.absoluteFilePath());
if (!f1.first || !file.open(QIODevice::ReadOnly)) {
QVariantMap map;
map.insert("code", FILE_NOT_FOUND_ERR);
map.insert("source", fileURI);
map.insert("target", _url);
_plugin->cb(_ecId, map);
emit done();
return;
}
QString content{file.readAll()};
request.setUrl(url);
if (url.password().size() || url.userName().size()) {
QString headerData = "Basic " + (url.userName() + ":" + url.password()).toLocal8Bit().toBase64();
request.setRawHeader("Authorization", headerData.toLocal8Bit());
}
SetHeaders(request, headers);
QString boundary = QString("CORDOVA-QT-%1A").arg(qrand());
while (content.contains(boundary)) {
boundary += QString("B%1A").arg(qrand());
}
request.setHeader(QNetworkRequest::ContentTypeHeader, QString("multipart/form-data; boundary=") + boundary);
fileKey.replace("\"", "");
fileName.replace("\"", "");
mimeType.replace("\"", "");
QString part = "--" + boundary + "\r\n";
part += "Content-Disposition: form-data; name=\"" + fileKey +"\"; filename=\"" + fileName + "\"\r\n";
part += "Content-Type: " + mimeType + "\r\n\r\n";
part += content + "\r\n";
for (QString key: params.keys()) {
part += "--" + boundary + "\r\n";
part += "Content-Disposition: form-data; name=\"" + key + "\";\r\n\r\n";
part += params.find(key)->toString();
part += "\r\n";
}
part += QString("--") + boundary + "--" + "\r\n";
_reply = QSharedPointer<QNetworkReply>(_manager.post(request, QByteArray(part.toUtf8())));
_reply->connect(_reply.data(), &QNetworkReply::finished, [this, content]() {
if (_reply->error() != QNetworkReply::NoError)
return;
int status = 200;
QVariant statusCode = _reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
if (statusCode.isValid()) {
status = statusCode.toInt();
}
QVariantMap map;
map.insert("responseCode", status);
map.insert("response", QString(_reply->readAll()));
map.insert("bytesSent", content.size());
_plugin->cb(_scId, map);
emit done();
});
_reply->connect(_reply.data(), SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(error(QNetworkReply::NetworkError)));
_reply->connect(_reply.data(), SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(progress(qint64, qint64)));
}
void FileTransferRequest::abort() {
QVariantMap map;
map.insert("code", ABORT_ERR);
_plugin->cb(_ecId, map);
_scId = 0;
emit done();
}
void FileTransferRequest::error(QNetworkReply::NetworkError code) {
Q_UNUSED(code);
int status = 404;
QVariant statusCode = _reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
if (statusCode.isValid()) {
status = statusCode.toInt();
}
QVariantMap map;
map.insert("http_status", status);
map.insert("body", QString(_reply->readAll()));
map.insert("code", CONNECTION_ERR);
_plugin->cb(_ecId, map);
emit done();
}
void FileTransferRequest::progress(qint64 bytesReceived, qint64 bytesTotal) {
QVariantMap map;
map.insert("lengthComputable", true);
map.insert("total", bytesTotal);
map.insert("loaded", bytesReceived);
if (bytesReceived && bytesTotal && _scId)
_plugin->callbackWithoutRemove(_scId, CordovaInternal::format(map));
}

View File

@@ -0,0 +1,103 @@
/*
*
* Copyright 2013 Canonical Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
#ifndef FILE_TRANSFER_H_SDASDASDAS
#define FILE_TRANSFER_H_SDASDASDAS
#include <QtCore>
#include <QtNetwork>
#include <cplugin.h>
class FileTransfer;
class FileTransferRequest: public QObject {
Q_OBJECT
QNetworkAccessManager &_manager;
int _scId, _ecId;
int _id;
QSharedPointer<QNetworkReply> _reply;
enum FileTransferError {
FILE_NOT_FOUND_ERR = 1,
INVALID_URL_ERR = 2,
CONNECTION_ERR = 3,
ABORT_ERR = 4
};
public:
FileTransferRequest(QNetworkAccessManager &manager, int scId, int ecId, int id, FileTransfer *plugin):
_manager(manager),
_scId(scId),
_ecId(ecId),
_id(id),
_plugin(plugin) {
}
void download(const QString& url, const QString &targetURI, const QVariantMap &headers);
void upload(const QString& _url, const QString& fileURI, QString fileKey, QString fileName, QString mimeType, const QVariantMap &params, const QVariantMap &headers);
void abort();
signals:
void done();
private slots:
void progress(qint64 bytesReceived, qint64 bytesTotal);
void error(QNetworkReply::NetworkError code);
private:
FileTransfer *_plugin;
Q_DISABLE_COPY(FileTransferRequest);
};
class FileTransfer : public CPlugin {
Q_OBJECT
public:
explicit FileTransfer(Cordova *cordova): CPlugin(cordova) {
}
Cordova* cordova() {
return m_cordova;
}
virtual const QString fullName() override {
return FileTransfer::fullID();
}
virtual const QString shortName() override {
return "FileTransfer";
}
static const QString fullID() {
return "FileTransfer";
}
public slots:
void abort(int scId, int ecId, int id);
void download(int scId, int ecId, const QString& url, const QString &target, bool /*trustAllHost*/, int id, const QVariantMap &/*headers*/);
void upload(int scId, int ecId, const QString &filePath, const QString& url, const QString& fileKey, const QString& fileName, const QString& mimeType,
const QVariantMap & params, bool /*trustAllHosts*/, bool /*chunkedMode*/, const QVariantMap &headers, int id, const QString &httpMethod);
private:
QNetworkAccessManager _manager;
QMultiMap<int, QSharedPointer<FileTransferRequest> > _id2request;
int lastRequestId;
};
#endif

View File

@@ -0,0 +1,579 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
/*jshint -W030 */
/*global Windows, WinJS*/
/*global module, require*/
var FTErr = require('./FileTransferError'),
ProgressEvent = require('cordova-plugin-file.ProgressEvent'),
FileUploadResult = require('cordova-plugin-file.FileUploadResult'),
FileProxy = require('cordova-plugin-file.FileProxy');
var appData = Windows.Storage.ApplicationData.current;
var LINE_START = "--";
var LINE_END = "\r\n";
var BOUNDARY = '+++++';
var fileTransferOps = [];
// Some private helper functions, hidden by the module
function cordovaPathToNative(path) {
var cleanPath = String(path);
// turn / into \\
cleanPath = cleanPath.replace(/\//g, '\\');
// turn \\ into \
cleanPath = cleanPath.replace(/\\\\/g, '\\');
// strip end \\ characters
cleanPath = cleanPath.replace(/\\+$/g, '');
return cleanPath;
}
function nativePathToCordova(path) {
return String(path).replace(/\\/g, '/');
}
function alreadyCancelled(opId) {
var op = fileTransferOps[opId];
return op && op.state === FileTransferOperation.CANCELLED;
}
function doUpload (upload, uploadId, filePath, server, successCallback, errorCallback) {
if (alreadyCancelled(uploadId)) {
errorCallback(new FTErr(FTErr.ABORT_ERR, nativePathToCordova(filePath), server));
return;
}
// update internal TransferOperation object with newly created promise
var uploadOperation = upload.startAsync();
fileTransferOps[uploadId].promise = uploadOperation;
uploadOperation.then(
function (result) {
// Update TransferOperation object with new state, delete promise property
// since it is not actual anymore
var currentUploadOp = fileTransferOps[uploadId];
if (currentUploadOp) {
currentUploadOp.state = FileTransferOperation.DONE;
currentUploadOp.promise = null;
}
var response = result.getResponseInformation();
var ftResult = new FileUploadResult(result.progress.bytesSent, response.statusCode, '');
// if server's response doesn't contain any data, then resolve operation now
if (result.progress.bytesReceived === 0) {
successCallback(ftResult);
return;
}
// otherwise create a data reader, attached to response stream to get server's response
var reader = new Windows.Storage.Streams.DataReader(result.getResultStreamAt(0));
reader.loadAsync(result.progress.bytesReceived).then(function (size) {
ftResult.response = reader.readString(size);
successCallback(ftResult);
reader.close();
});
},
function (error) {
var source = nativePathToCordova(filePath);
// Handle download error here.
// Wrap this routines into promise due to some async methods
var getTransferError = new WinJS.Promise(function (resolve) {
if (error.message === 'Canceled') {
// If download was cancelled, message property will be specified
resolve(new FTErr(FTErr.ABORT_ERR, source, server, null, null, error));
} else {
// in the other way, try to get response property
var response = upload.getResponseInformation();
if (!response) {
resolve(new FTErr(FTErr.CONNECTION_ERR, source, server));
} else {
var reader = new Windows.Storage.Streams.DataReader(upload.getResultStreamAt(0));
reader.loadAsync(upload.progress.bytesReceived).then(function (size) {
var responseText = reader.readString(size);
resolve(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, server, response.statusCode, responseText, error));
reader.close();
});
}
}
});
// Update TransferOperation object with new state, delete promise property
// since it is not actual anymore
var currentUploadOp = fileTransferOps[uploadId];
if (currentUploadOp) {
currentUploadOp.state = FileTransferOperation.CANCELLED;
currentUploadOp.promise = null;
}
// Report the upload error back
getTransferError.then(function (transferError) {
errorCallback(transferError);
});
},
function (evt) {
var progressEvent = new ProgressEvent('progress', {
loaded: evt.progress.bytesSent,
total: evt.progress.totalBytesToSend,
target: evt.resultFile
});
progressEvent.lengthComputable = true;
successCallback(progressEvent, { keepCallback: true });
}
);
}
function FileTransferOperation(state, promise) {
this.state = state;
this.promise = promise;
}
FileTransferOperation.PENDING = 0;
FileTransferOperation.DONE = 1;
FileTransferOperation.CANCELLED = 2;
var HTTP_E_STATUS_NOT_MODIFIED = -2145844944;
module.exports = {
/*
exec(win, fail, 'FileTransfer', 'upload',
[filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id, httpMethod]);
*/
upload: function (successCallback, errorCallback, options) {
var filePath = options[0];
var server = options[1];
var fileKey = options[2] || 'source';
var fileName = options[3];
var mimeType = options[4];
var params = options[5];
// var trustAllHosts = options[6]; // todo
// var chunkedMode = options[7]; // todo
var headers = options[8] || {};
var uploadId = options[9];
var httpMethod = options[10];
var isMultipart = typeof headers["Content-Type"] === 'undefined';
function stringToByteArray(str) {
var byteCharacters = atob(str);
var byteNumbers = new Array(byteCharacters.length);
for (var i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
return new Uint8Array(byteNumbers);
}
if (!filePath || (typeof filePath !== 'string')) {
errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, null, server));
return;
}
if (filePath.indexOf("data:") === 0 && filePath.indexOf("base64") !== -1) {
// First a DataWriter object is created, backed by an in-memory stream where
// the data will be stored.
var writer = Windows.Storage.Streams.DataWriter(new Windows.Storage.Streams.InMemoryRandomAccessStream());
writer.unicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.utf8;
writer.byteOrder = Windows.Storage.Streams.ByteOrder.littleEndian;
var commaIndex = filePath.indexOf(",");
if (commaIndex === -1) {
errorCallback(new FTErr(FTErr.INVALID_URL_ERR, fileName, server, null, null, "No comma in data: URI"));
return;
}
// Create internal download operation object
fileTransferOps[uploadId] = new FileTransferOperation(FileTransferOperation.PENDING, null);
var fileDataString = filePath.substr(commaIndex + 1);
// setting request headers for uploader
var uploader = new Windows.Networking.BackgroundTransfer.BackgroundUploader();
uploader.method = httpMethod;
for (var header in headers) {
if (headers.hasOwnProperty(header)) {
uploader.setRequestHeader(header, headers[header]);
}
}
if (isMultipart) {
// adding params supplied to request payload
var multipartParams = '';
for (var key in params) {
if (params.hasOwnProperty(key)) {
multipartParams += LINE_START + BOUNDARY + LINE_END;
multipartParams += "Content-Disposition: form-data; name=\"" + key + "\"";
multipartParams += LINE_END + LINE_END;
multipartParams += params[key];
multipartParams += LINE_END;
}
}
var multipartFile = LINE_START + BOUNDARY + LINE_END;
multipartFile += "Content-Disposition: form-data; name=\"file\";";
multipartFile += " filename=\"" + fileName + "\"" + LINE_END;
multipartFile += "Content-Type: " + mimeType + LINE_END + LINE_END;
var bound = LINE_END + LINE_START + BOUNDARY + LINE_START + LINE_END;
uploader.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
writer.writeString(multipartParams);
writer.writeString(multipartFile);
writer.writeBytes(stringToByteArray(fileDataString));
writer.writeString(bound);
} else {
writer.writeBytes(stringToByteArray(fileDataString));
}
var stream;
// The call to store async sends the actual contents of the writer
// to the backing stream.
writer.storeAsync().then(function () {
// For the in-memory stream implementation we are using, the flushAsync call
// is superfluous, but other types of streams may require it.
return writer.flushAsync();
}).then(function () {
// We detach the stream to prolong its useful lifetime. Were we to fail
// to detach the stream, the call to writer.close() would close the underlying
// stream, preventing its subsequent use by the DataReader below. Most clients
// of DataWriter will have no reason to use the underlying stream after
// writer.close() is called, and will therefore have no reason to call
// writer.detachStream(). Note that once we detach the stream, we assume
// responsibility for closing the stream subsequently; after the stream
// has been detached, a call to writer.close() will have no effect on the stream.
stream = writer.detachStream();
// Make sure the stream is read from the beginning in the reader
// we are creating below.
stream.seek(0);
// Most DataWriter clients will not call writer.detachStream(),
// and furthermore will be working with a file-backed or network-backed stream,
// rather than an in-memory-stream. In such cases, it would be particularly
// important to call writer.close(). Doing so is always a best practice.
writer.close();
if (alreadyCancelled(uploadId)) {
errorCallback(new FTErr(FTErr.ABORT_ERR, nativePathToCordova(filePath), server));
return;
}
// create download object. This will throw an exception if URL is malformed
var uri = new Windows.Foundation.Uri(server);
var createUploadOperation;
try {
createUploadOperation = uploader.createUploadFromStreamAsync(uri, stream);
} catch (e) {
errorCallback(new FTErr(FTErr.INVALID_URL_ERR));
return;
}
createUploadOperation.then(
function (upload) {
doUpload(upload, uploadId, filePath, server, successCallback, errorCallback);
},
function (err) {
var errorObj = new FTErr(FTErr.INVALID_URL_ERR);
errorObj.exception = err;
errorCallback(errorObj);
});
});
return;
}
if (filePath.substr(0, 8) === "file:///") {
filePath = appData.localFolder.path + filePath.substr(8).split("/").join("\\");
} else if (filePath.indexOf('ms-appdata:///') === 0) {
// Handle 'ms-appdata' scheme
filePath = filePath.replace('ms-appdata:///local', appData.localFolder.path)
.replace('ms-appdata:///temp', appData.temporaryFolder.path);
} else if (filePath.indexOf('cdvfile://') === 0) {
filePath = filePath.replace('cdvfile://localhost/persistent', appData.localFolder.path)
.replace('cdvfile://localhost/temporary', appData.temporaryFolder.path);
}
// normalize path separators
filePath = cordovaPathToNative(filePath);
// Create internal download operation object
fileTransferOps[uploadId] = new FileTransferOperation(FileTransferOperation.PENDING, null);
Windows.Storage.StorageFile.getFileFromPathAsync(filePath)
.then(function (storageFile) {
if (!fileName) {
fileName = storageFile.name;
}
if (!mimeType) {
// use the actual content type of the file, probably this should be the default way.
// other platforms probably can't look this up.
mimeType = storageFile.contentType;
}
if (alreadyCancelled(uploadId)) {
errorCallback(new FTErr(FTErr.ABORT_ERR, nativePathToCordova(filePath), server));
return;
}
// setting request headers for uploader
var uploader = new Windows.Networking.BackgroundTransfer.BackgroundUploader();
uploader.method = httpMethod;
for (var header in headers) {
if (headers.hasOwnProperty(header)) {
uploader.setRequestHeader(header, headers[header]);
}
}
// create download object. This will throw an exception if URL is malformed
var uri = new Windows.Foundation.Uri(server);
var createUploadOperation;
try {
if (isMultipart) {
// adding params supplied to request payload
var transferParts = [];
for (var key in params) {
// Create content part for params only if value is specified because CreateUploadAsync fails otherwise
if (params.hasOwnProperty(key) && params[key] !== null && params[key] !== undefined && params[key].toString() !== "") {
var contentPart = new Windows.Networking.BackgroundTransfer.BackgroundTransferContentPart();
contentPart.setHeader("Content-Disposition", "form-data; name=\"" + key + "\"");
contentPart.setText(params[key]);
transferParts.push(contentPart);
}
}
// Adding file to upload to request payload
var fileToUploadPart = new Windows.Networking.BackgroundTransfer.BackgroundTransferContentPart(fileKey, fileName);
fileToUploadPart.setHeader("Content-Type", mimeType);
fileToUploadPart.setFile(storageFile);
transferParts.push(fileToUploadPart);
createUploadOperation = uploader.createUploadAsync(uri, transferParts);
} else {
createUploadOperation = WinJS.Promise.wrap(uploader.createUpload(uri, storageFile));
}
} catch (e) {
errorCallback(new FTErr(FTErr.INVALID_URL_ERR));
return;
}
createUploadOperation.then(
function (upload) {
doUpload(upload, uploadId, filePath, server, successCallback, errorCallback);
},
function (err) {
var errorObj = new FTErr(FTErr.INVALID_URL_ERR);
errorObj.exception = err;
errorCallback(errorObj);
}
);
}, function (err) {
errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, fileName, server, null, null, err));
});
},
// [source, target, trustAllHosts, id, headers]
download:function(successCallback, errorCallback, options) {
var source = options[0];
var target = options[1];
var downloadId = options[3];
var headers = options[4] || {};
if (!target) {
errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR));
return;
}
if (target.substr(0, 8) === "file:///") {
target = appData.localFolder.path + target.substr(8).split("/").join("\\");
} else if (target.indexOf('ms-appdata:///') === 0) {
// Handle 'ms-appdata' scheme
target = target.replace('ms-appdata:///local', appData.localFolder.path)
.replace('ms-appdata:///temp', appData.temporaryFolder.path);
} else if (target.indexOf('cdvfile://') === 0) {
target = target.replace('cdvfile://localhost/persistent', appData.localFolder.path)
.replace('cdvfile://localhost/temporary', appData.temporaryFolder.path);
}
target = cordovaPathToNative(target);
var path = target.substr(0, target.lastIndexOf("\\"));
var fileName = target.substr(target.lastIndexOf("\\") + 1);
if (path === null || fileName === null) {
errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR));
return;
}
// Download to a temp file to avoid the file deletion on 304
// CB-7006 Empty file is created on file transfer if server response is 304
var tempFileName = '~' + fileName;
var download = null;
// Create internal download operation object
fileTransferOps[downloadId] = new FileTransferOperation(FileTransferOperation.PENDING, null);
var downloadCallback = function(storageFolder) {
storageFolder.createFileAsync(tempFileName, Windows.Storage.CreationCollisionOption.replaceExisting).then(function (storageFile) {
if (alreadyCancelled(downloadId)) {
errorCallback(new FTErr(FTErr.ABORT_ERR, source, target));
return;
}
// if download isn't cancelled, contunue with creating and preparing download operation
var downloader = new Windows.Networking.BackgroundTransfer.BackgroundDownloader();
for (var header in headers) {
if (headers.hasOwnProperty(header)) {
downloader.setRequestHeader(header, headers[header]);
}
}
// create download object. This will throw an exception if URL is malformed
try {
var uri = Windows.Foundation.Uri(source);
download = downloader.createDownload(uri, storageFile);
} catch (e) {
// so we handle this and call errorCallback
errorCallback(new FTErr(FTErr.INVALID_URL_ERR));
return;
}
var downloadOperation = download.startAsync();
// update internal TransferOperation object with newly created promise
fileTransferOps[downloadId].promise = downloadOperation;
downloadOperation.then(function () {
// Update TransferOperation object with new state, delete promise property
// since it is not actual anymore
var currentDownloadOp = fileTransferOps[downloadId];
if (currentDownloadOp) {
currentDownloadOp.state = FileTransferOperation.DONE;
currentDownloadOp.promise = null;
}
storageFile.renameAsync(fileName, Windows.Storage.CreationCollisionOption.replaceExisting).done(function () {
var nativeURI = storageFile.path.replace(appData.localFolder.path, 'ms-appdata:///local')
.replace(appData.temporaryFolder.path, 'ms-appdata:///temp')
.replace(/\\/g, '/');
// Passing null as error callback here because downloaded file should exist in any case
// otherwise the error callback will be hit during file creation in another place
FileProxy.resolveLocalFileSystemURI(successCallback, null, [nativeURI]);
}, function(error) {
errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, null, null, error));
});
}, function(error) {
var getTransferError = new WinJS.Promise(function (resolve) {
// Handle download error here. If download was cancelled,
// message property will be specified
if (error.message === 'Canceled') {
resolve(new FTErr(FTErr.ABORT_ERR, source, target, null, null, error));
} else if (error && error.number === HTTP_E_STATUS_NOT_MODIFIED) {
resolve(new FTErr(FTErr.NOT_MODIFIED_ERR, source, target, 304, null, error));
} else {
// in the other way, try to get response property
var response = download.getResponseInformation();
if (!response) {
resolve(new FTErr(FTErr.CONNECTION_ERR, source, target));
} else {
var reader = new Windows.Storage.Streams.DataReader(download.getResultStreamAt(0));
reader.loadAsync(download.progress.bytesReceived).then(function (bytesLoaded) {
var payload = reader.readString(bytesLoaded);
resolve(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, response.statusCode, payload, error));
});
}
}
});
getTransferError.then(function (fileTransferError) {
// Update TransferOperation object with new state, delete promise property
// since it is not actual anymore
var currentDownloadOp = fileTransferOps[downloadId];
if (currentDownloadOp) {
currentDownloadOp.state = FileTransferOperation.CANCELLED;
currentDownloadOp.promise = null;
}
// Cleanup, remove incompleted file
storageFile.deleteAsync().then(function() {
errorCallback(fileTransferError);
});
});
}, function(evt) {
var progressEvent = new ProgressEvent('progress', {
loaded: evt.progress.bytesReceived,
total: evt.progress.totalBytesToReceive,
target: evt.resultFile
});
// when bytesReceived == 0, BackgroundDownloader has not yet differentiated whether it could get file length or not,
// when totalBytesToReceive == 0, BackgroundDownloader is unable to get file length
progressEvent.lengthComputable = (evt.progress.bytesReceived > 0) && (evt.progress.totalBytesToReceive > 0);
successCallback(progressEvent, { keepCallback: true });
});
}, function(error) {
errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, null, null, error));
});
};
var fileNotFoundErrorCallback = function(error) {
errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, null, null, error));
};
Windows.Storage.StorageFolder.getFolderFromPathAsync(path).then(downloadCallback, function (error) {
// Handle non-existent directory
if (error.number === -2147024894) {
var parent = path.substr(0, path.lastIndexOf('\\')),
folderNameToCreate = path.substr(path.lastIndexOf('\\') + 1);
Windows.Storage.StorageFolder.getFolderFromPathAsync(parent).then(function(parentFolder) {
parentFolder.createFolderAsync(folderNameToCreate).then(downloadCallback, fileNotFoundErrorCallback);
}, fileNotFoundErrorCallback);
} else {
fileNotFoundErrorCallback();
}
});
},
abort: function (successCallback, error, options) {
var fileTransferOpId = options[0];
// Try to find transferOperation with id specified, and cancel its' promise
var currentOp = fileTransferOps[fileTransferOpId];
if (currentOp) {
currentOp.state = FileTransferOperation.CANCELLED;
currentOp.promise && currentOp.promise.cancel();
} else if (typeof fileTransferOpId !== 'undefined') {
// Create the operation in cancelled state to be aborted right away
fileTransferOps[fileTransferOpId] = new FileTransferOperation(FileTransferOperation.CANCELLED, null);
}
}
};
require("cordova/exec/proxy").add("FileTransfer",module.exports);

View File

@@ -0,0 +1,994 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using Microsoft.Phone.Controls;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using System.Windows;
using System.Security;
using System.Diagnostics;
using System.Threading.Tasks;
using WPCordovaClassLib.Cordova.JSON;
using System.Reflection;
namespace WPCordovaClassLib.Cordova.Commands
{
public class FileTransfer : BaseCommand
{
public class DownloadRequestState
{
// This class stores the State of the request.
public HttpWebRequest request;
public TransferOptions options;
public bool isCancelled;
public DownloadRequestState()
{
request = null;
options = null;
isCancelled = false;
}
}
public class TransferOptions
{
/// File path to upload OR File path to download to
public string FilePath { get; set; }
public string Url { get; set; }
/// Flag to recognize if we should trust every host (only in debug environments)
public bool TrustAllHosts { get; set; }
public string Id { get; set; }
public string Headers { get; set; }
public string CallbackId { get; set; }
public bool ChunkedMode { get; set; }
/// Server address
public string Server { get; set; }
/// File key
public string FileKey { get; set; }
/// File name on the server
public string FileName { get; set; }
/// File Mime type
public string MimeType { get; set; }
/// Additional options
public string Params { get; set; }
public string Method { get; set; }
public TransferOptions()
{
FileKey = "file";
FileName = "image.jpg";
MimeType = "image/jpeg";
}
}
/// <summary>
/// Boundary symbol
/// </summary>
private string Boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");
// Error codes
public const int FileNotFoundError = 1;
public const int InvalidUrlError = 2;
public const int ConnectionError = 3;
public const int AbortError = 4; // not really an error, but whatevs
private static Dictionary<string, DownloadRequestState> InProcDownloads = new Dictionary<string,DownloadRequestState>();
// Private instance of the main WebBrowser instance
// NOTE: Any access to this object needs to occur on the UI thread via the Dispatcher
private WebBrowser browser;
/// <summary>
/// Uploading response info
/// </summary>
[DataContract]
public class FileUploadResult
{
/// <summary>
/// Amount of sent bytes
/// </summary>
[DataMember(Name = "bytesSent")]
public long BytesSent { get; set; }
/// <summary>
/// Server response code
/// </summary>
[DataMember(Name = "responseCode")]
public long ResponseCode { get; set; }
/// <summary>
/// Server response
/// </summary>
[DataMember(Name = "response", EmitDefaultValue = false)]
public string Response { get; set; }
/// <summary>
/// Creates FileUploadResult object with response values
/// </summary>
/// <param name="bytesSent">Amount of sent bytes</param>
/// <param name="responseCode">Server response code</param>
/// <param name="response">Server response</param>
public FileUploadResult(long bytesSent, long responseCode, string response)
{
this.BytesSent = bytesSent;
this.ResponseCode = responseCode;
this.Response = response;
}
}
/// <summary>
/// Represents transfer error codes for callback
/// </summary>
[DataContract]
public class FileTransferError
{
/// <summary>
/// Error code
/// </summary>
[DataMember(Name = "code", IsRequired = true)]
public int Code { get; set; }
/// <summary>
/// The source URI
/// </summary>
[DataMember(Name = "source", IsRequired = true)]
public string Source { get; set; }
/// <summary>
/// The target URI
/// </summary>
///
[DataMember(Name = "target", IsRequired = true)]
public string Target { get; set; }
[DataMember(Name = "body", IsRequired = true)]
public string Body { get; set; }
/// <summary>
/// The http status code response from the remote URI
/// </summary>
[DataMember(Name = "http_status", IsRequired = true)]
public int HttpStatus { get; set; }
/// <summary>
/// Creates FileTransferError object
/// </summary>
/// <param name="errorCode">Error code</param>
public FileTransferError(int errorCode)
{
this.Code = errorCode;
this.Source = null;
this.Target = null;
this.HttpStatus = 0;
this.Body = "";
}
public FileTransferError(int errorCode, string source, string target, int status, string body = "")
{
this.Code = errorCode;
this.Source = source;
this.Target = target;
this.HttpStatus = status;
this.Body = body;
}
}
/// <summary>
/// Represents a singular progress event to be passed back to javascript
/// </summary>
[DataContract]
public class FileTransferProgress
{
/// <summary>
/// Is the length of the response known?
/// </summary>
[DataMember(Name = "lengthComputable", IsRequired = true)]
public bool LengthComputable { get; set; }
/// <summary>
/// amount of bytes loaded
/// </summary>
[DataMember(Name = "loaded", IsRequired = true)]
public long BytesLoaded { get; set; }
/// <summary>
/// Total bytes
/// </summary>
[DataMember(Name = "total", IsRequired = false)]
public long BytesTotal { get; set; }
public FileTransferProgress(long bTotal = 0, long bLoaded = 0)
{
LengthComputable = bTotal > 0;
BytesLoaded = bLoaded;
BytesTotal = bTotal;
}
}
/// <summary>
/// Represents a request header passed from Javascript to upload/download operations
/// </summary>
[DataContract]
protected struct Header
{
[DataMember(Name = "name")]
public string Name;
[DataMember(Name = "value")]
public string Value;
}
private static MethodInfo JsonDeserializeUsingJsonNet;
public FileTransfer()
{
if (JsonDeserializeUsingJsonNet == null)
{
var method = typeof(JsonHelper).GetMethod("Deserialize", new Type[] { typeof(string), typeof(bool) });
if (method != null)
{
JsonDeserializeUsingJsonNet = method.MakeGenericMethod(new Type[] { typeof(Header[]) });
}
}
}
/// Helper method to copy all relevant cookies from the WebBrowser control into a header on
/// the HttpWebRequest
/// </summary>
/// <param name="browser">The source browser to copy the cookies from</param>
/// <param name="webRequest">The destination HttpWebRequest to add the cookie header to</param>
/// <returns>Nothing</returns>
private async Task CopyCookiesFromWebBrowser(HttpWebRequest webRequest)
{
var tcs = new TaskCompletionSource<object>();
// Accessing WebBrowser needs to happen on the UI thread
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
// Get the WebBrowser control
if (this.browser == null)
{
PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
if (frame != null)
{
PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
if (page != null)
{
CordovaView cView = page.FindName("CordovaView") as CordovaView;
if (cView != null)
{
this.browser = cView.Browser;
}
}
}
}
try
{
// Only copy the cookies if the scheme and host match (to avoid any issues with secure/insecure cookies)
// NOTE: since the returned CookieCollection appears to munge the original cookie's domain value in favor of the actual Source domain,
// we can't know for sure whether the cookies would be applicable to any other hosts, so best to play it safe and skip for now.
if (this.browser != null && this.browser.Source.IsAbsoluteUri == true &&
this.browser.Source.Scheme == webRequest.RequestUri.Scheme && this.browser.Source.Host == webRequest.RequestUri.Host)
{
string cookieHeader = "";
string requestPath = webRequest.RequestUri.PathAndQuery;
CookieCollection cookies = this.browser.GetCookies();
// Iterate over the cookies and add to the header
foreach (Cookie cookie in cookies)
{
// Check that the path is allowed, first
// NOTE: Path always seems to be empty for now, even if the cookie has a path set by the server.
if (cookie.Path.Length == 0 || requestPath.IndexOf(cookie.Path, StringComparison.InvariantCultureIgnoreCase) == 0)
{
cookieHeader += cookie.Name + "=" + cookie.Value + "; ";
}
}
// Finally, set the header if we found any cookies
if (cookieHeader.Length > 0)
{
webRequest.Headers["Cookie"] = cookieHeader;
}
}
}
catch (Exception)
{
// Swallow the exception
}
// Complete the task
tcs.SetResult(Type.Missing);
});
await tcs.Task;
}
/// <summary>
/// Upload options
/// </summary>
//private TransferOptions uploadOptions;
/// <summary>
/// Bytes sent
/// </summary>
private long bytesSent;
/// <summary>
/// sends a file to a server
/// </summary>
/// <param name="options">Upload options</param>
/// exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id, httpMethod]);
public void upload(string options)
{
options = options.Replace("{}", ""); // empty objects screw up the Deserializer
string callbackId = "";
TransferOptions uploadOptions = null;
HttpWebRequest webRequest = null;
try
{
try
{
string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
uploadOptions = new TransferOptions();
uploadOptions.FilePath = args[0];
uploadOptions.Server = args[1];
uploadOptions.FileKey = args[2];
uploadOptions.FileName = args[3];
uploadOptions.MimeType = args[4];
uploadOptions.Params = args[5];
bool trustAll = false;
bool.TryParse(args[6],out trustAll);
uploadOptions.TrustAllHosts = trustAll;
bool doChunked = false;
bool.TryParse(args[7], out doChunked);
uploadOptions.ChunkedMode = doChunked;
//8 : Headers
//9 : id
//10: method
uploadOptions.Headers = args[8];
uploadOptions.Id = args[9];
uploadOptions.Method = args[10];
uploadOptions.CallbackId = callbackId = args[11];
}
catch (Exception)
{
DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
return;
}
Uri serverUri;
try
{
serverUri = new Uri(uploadOptions.Server);
}
catch (Exception)
{
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(InvalidUrlError, uploadOptions.Server, null, 0)));
return;
}
webRequest = (HttpWebRequest)WebRequest.Create(serverUri);
webRequest.ContentType = "multipart/form-data; boundary=" + Boundary;
webRequest.Method = uploadOptions.Method;
DownloadRequestState reqState = new DownloadRequestState();
InProcDownloads[uploadOptions.Id] = reqState;
reqState.options = uploadOptions;
reqState.request = webRequest;
try
{
// Associate cookies with the request
// This is an async call, so we need to await it in order to preserve proper control flow
Task cookieTask = CopyCookiesFromWebBrowser(webRequest);
cookieTask.Wait();
}
catch (AggregateException ae)
{
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR,
new FileTransferError(FileTransfer.ConnectionError, uploadOptions.FilePath, uploadOptions.Server, 0, ae.InnerException.Message)));
return;
}
if (!string.IsNullOrEmpty(uploadOptions.Headers))
{
Dictionary<string, string> headers = parseHeaders(uploadOptions.Headers);
if (headers != null)
{
foreach (string key in headers.Keys)
{
webRequest.Headers[key] = headers[key];
}
}
}
webRequest.BeginGetRequestStream(uploadCallback, reqState);
}
catch (Exception /*ex*/)
{
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(ConnectionError)),callbackId);
}
}
// example : "{\"Authorization\":\"Basic Y29yZG92YV91c2VyOmNvcmRvdmFfcGFzc3dvcmQ=\"}"
protected Dictionary<string,string> parseHeaders(string jsonHeaders)
{
try
{
if (FileTransfer.JsonDeserializeUsingJsonNet != null)
{
return ((Header[])FileTransfer.JsonDeserializeUsingJsonNet.Invoke(null, new object[] { jsonHeaders, true }))
.ToDictionary(header => header.Name, header => header.Value);
}
else
{
return JsonHelper.Deserialize<Header[]>(jsonHeaders)
.ToDictionary(header => header.Name, header => header.Value);
}
}
catch (Exception)
{
Debug.WriteLine("Failed to parseHeaders from string :: " + jsonHeaders);
}
return new Dictionary<string, string>();
}
public void download(string options)
{
TransferOptions downloadOptions = null;
HttpWebRequest webRequest = null;
string callbackId;
try
{
// source, target, trustAllHosts, this._id, headers
string[] optionStrings = JSON.JsonHelper.Deserialize<string[]>(options);
downloadOptions = new TransferOptions();
downloadOptions.Url = optionStrings[0];
downloadOptions.FilePath = optionStrings[1];
bool trustAll = false;
bool.TryParse(optionStrings[2],out trustAll);
downloadOptions.TrustAllHosts = trustAll;
downloadOptions.Id = optionStrings[3];
downloadOptions.Headers = optionStrings[4];
downloadOptions.CallbackId = callbackId = optionStrings[5];
}
catch (Exception)
{
DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
return;
}
try
{
// is the URL a local app file?
if (downloadOptions.Url.StartsWith("x-wmapp0") || downloadOptions.Url.StartsWith("file:"))
{
using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
{
string cleanUrl = downloadOptions.Url.Replace("x-wmapp0:", "").Replace("file:", "").Replace("//","");
// pre-emptively create any directories in the FilePath that do not exist
string directoryName = getDirectoryName(downloadOptions.FilePath);
if (!string.IsNullOrEmpty(directoryName) && !isoFile.DirectoryExists(directoryName))
{
isoFile.CreateDirectory(directoryName);
}
// just copy from one area of iso-store to another ...
if (isoFile.FileExists(downloadOptions.Url))
{
isoFile.CopyFile(downloadOptions.Url, downloadOptions.FilePath);
}
else
{
// need to unpack resource from the dll
Uri uri = new Uri(cleanUrl, UriKind.Relative);
var resource = Application.GetResourceStream(uri);
if (resource != null)
{
// create the file destination
if (!isoFile.FileExists(downloadOptions.FilePath))
{
var destFile = isoFile.CreateFile(downloadOptions.FilePath);
destFile.Close();
}
using (FileStream fileStream = new IsolatedStorageFileStream(downloadOptions.FilePath, FileMode.Open, FileAccess.Write, isoFile))
{
long totalBytes = resource.Stream.Length;
int bytesRead = 0;
using (BinaryReader reader = new BinaryReader(resource.Stream))
{
using (BinaryWriter writer = new BinaryWriter(fileStream))
{
int BUFFER_SIZE = 1024;
byte[] buffer;
while (true)
{
buffer = reader.ReadBytes(BUFFER_SIZE);
// fire a progress event ?
bytesRead += buffer.Length;
if (buffer.Length > 0)
{
writer.Write(buffer);
DispatchFileTransferProgress(bytesRead, totalBytes, callbackId);
}
else
{
writer.Close();
reader.Close();
fileStream.Close();
break;
}
}
}
}
}
}
}
}
File.FileEntry entry = File.FileEntry.GetEntry(downloadOptions.FilePath);
if (entry != null)
{
DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry), callbackId);
}
else
{
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, File.NOT_FOUND_ERR), callbackId);
}
return;
}
else
{
// otherwise it is web-bound, we will actually download it
//Debug.WriteLine("Creating WebRequest for url : " + downloadOptions.Url);
webRequest = (HttpWebRequest)WebRequest.Create(downloadOptions.Url);
}
}
catch (Exception /*ex*/)
{
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR,
new FileTransferError(InvalidUrlError, downloadOptions.Url, null, 0)));
return;
}
if (downloadOptions != null && webRequest != null)
{
DownloadRequestState state = new DownloadRequestState();
state.options = downloadOptions;
state.request = webRequest;
InProcDownloads[downloadOptions.Id] = state;
try
{
// Associate cookies with the request
// This is an async call, so we need to await it in order to preserve proper control flow
Task cookieTask = CopyCookiesFromWebBrowser(webRequest);
cookieTask.Wait();
}
catch (AggregateException ae)
{
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR,
new FileTransferError(FileTransfer.ConnectionError, downloadOptions.Url, downloadOptions.FilePath, 0, ae.InnerException.Message)));
return;
}
if (!string.IsNullOrEmpty(downloadOptions.Headers))
{
Dictionary<string, string> headers = parseHeaders(downloadOptions.Headers);
foreach (string key in headers.Keys)
{
webRequest.Headers[key] = headers[key];
}
}
try
{
webRequest.BeginGetResponse(new AsyncCallback(downloadCallback), state);
}
catch (WebException)
{
// eat it
}
// dispatch an event for progress ( 0 )
lock (state)
{
if (!state.isCancelled)
{
var plugRes = new PluginResult(PluginResult.Status.OK, new FileTransferProgress());
plugRes.KeepCallback = true;
plugRes.CallbackId = callbackId;
DispatchCommandResult(plugRes, callbackId);
}
}
}
}
public void abort(string options)
{
Debug.WriteLine("Abort :: " + options);
string[] optionStrings = JSON.JsonHelper.Deserialize<string[]>(options);
string id = optionStrings[0];
string callbackId = optionStrings[1];
if (id != null && InProcDownloads.ContainsKey(id))
{
DownloadRequestState state = InProcDownloads[id];
if (!state.isCancelled)
{ // prevent multiple callbacks for the same abort
state.isCancelled = true;
if (!state.request.HaveResponse)
{
state.request.Abort();
InProcDownloads.Remove(id);
//callbackId = state.options.CallbackId;
//state = null;
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR,
new FileTransferError(FileTransfer.AbortError)),
state.options.CallbackId);
}
}
}
else
{
DispatchCommandResult(new PluginResult(PluginResult.Status.IO_EXCEPTION), callbackId); // TODO: is it an IO exception?
}
}
private void DispatchFileTransferProgress(long bytesLoaded, long bytesTotal, string callbackId, bool keepCallback = true)
{
Debug.WriteLine("DispatchFileTransferProgress : " + callbackId);
// send a progress change event
FileTransferProgress progEvent = new FileTransferProgress(bytesTotal);
progEvent.BytesLoaded = bytesLoaded;
PluginResult plugRes = new PluginResult(PluginResult.Status.OK, progEvent);
plugRes.KeepCallback = keepCallback;
plugRes.CallbackId = callbackId;
DispatchCommandResult(plugRes, callbackId);
}
/// <summary>
///
/// </summary>
/// <param name="asynchronousResult"></param>
private void downloadCallback(IAsyncResult asynchronousResult)
{
DownloadRequestState reqState = (DownloadRequestState)asynchronousResult.AsyncState;
HttpWebRequest request = reqState.request;
string callbackId = reqState.options.CallbackId;
try
{
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
// send a progress change event
DispatchFileTransferProgress(0, response.ContentLength, callbackId);
using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
{
// create any directories in the path that do not exist
string directoryName = getDirectoryName(reqState.options.FilePath);
if (!string.IsNullOrEmpty(directoryName) && !isoFile.DirectoryExists(directoryName))
{
isoFile.CreateDirectory(directoryName);
}
// create the file if not exists
if (!isoFile.FileExists(reqState.options.FilePath))
{
var file = isoFile.CreateFile(reqState.options.FilePath);
file.Close();
}
using (FileStream fileStream = new IsolatedStorageFileStream(reqState.options.FilePath, FileMode.Open, FileAccess.Write, isoFile))
{
long totalBytes = response.ContentLength;
int bytesRead = 0;
using (BinaryReader reader = new BinaryReader(response.GetResponseStream()))
{
using (BinaryWriter writer = new BinaryWriter(fileStream))
{
int BUFFER_SIZE = 1024;
byte[] buffer;
while (true)
{
buffer = reader.ReadBytes(BUFFER_SIZE);
// fire a progress event ?
bytesRead += buffer.Length;
if (buffer.Length > 0 && !reqState.isCancelled)
{
writer.Write(buffer);
DispatchFileTransferProgress(bytesRead, totalBytes, callbackId);
}
else
{
writer.Close();
reader.Close();
fileStream.Close();
break;
}
System.Threading.Thread.Sleep(1);
}
}
}
}
if (reqState.isCancelled)
{
isoFile.DeleteFile(reqState.options.FilePath);
}
}
if (reqState.isCancelled)
{
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(AbortError)),
callbackId);
}
else
{
File.FileEntry entry = new File.FileEntry(reqState.options.FilePath);
DispatchCommandResult(new PluginResult(PluginResult.Status.OK, entry), callbackId);
}
}
catch (IsolatedStorageException)
{
// Trying to write the file somewhere within the IsoStorage.
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError)),
callbackId);
}
catch (SecurityException)
{
// Trying to write the file somewhere not allowed.
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError)),
callbackId);
}
catch (WebException webex)
{
// TODO: probably need better work here to properly respond with all http status codes back to JS
// Right now am jumping through hoops just to detect 404.
HttpWebResponse response = (HttpWebResponse)webex.Response;
if ((webex.Status == WebExceptionStatus.ProtocolError && response.StatusCode == HttpStatusCode.NotFound)
|| webex.Status == WebExceptionStatus.UnknownError)
{
// Weird MSFT detection of 404... seriously... just give us the f(*&#$@ status code as a number ffs!!!
// "Numbers for HTTP status codes? Nah.... let's create our own set of enums/structs to abstract that stuff away."
// FACEPALM
// Or just cast it to an int, whiner ... -jm
int statusCode = (int)response.StatusCode;
string body = "";
using (Stream streamResponse = response.GetResponseStream())
{
using (StreamReader streamReader = new StreamReader(streamResponse))
{
body = streamReader.ReadToEnd();
}
}
FileTransferError ftError = new FileTransferError(ConnectionError, null, null, statusCode, body);
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ftError),
callbackId);
}
else
{
lock (reqState)
{
if (!reqState.isCancelled)
{
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR,
new FileTransferError(ConnectionError)),
callbackId);
}
else
{
Debug.WriteLine("It happened");
}
}
}
}
catch (Exception)
{
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR,
new FileTransferError(FileNotFoundError)),
callbackId);
}
//System.Threading.Thread.Sleep(1000);
if (InProcDownloads.ContainsKey(reqState.options.Id))
{
InProcDownloads.Remove(reqState.options.Id);
}
}
/// <summary>
/// Read file from Isolated Storage and sends it to server
/// </summary>
/// <param name="asynchronousResult"></param>
private void uploadCallback(IAsyncResult asynchronousResult)
{
DownloadRequestState reqState = (DownloadRequestState)asynchronousResult.AsyncState;
HttpWebRequest webRequest = reqState.request;
string callbackId = reqState.options.CallbackId;
try
{
using (Stream requestStream = (webRequest.EndGetRequestStream(asynchronousResult)))
{
string lineStart = "--";
string lineEnd = Environment.NewLine;
byte[] boundaryBytes = System.Text.Encoding.UTF8.GetBytes(lineStart + Boundary + lineEnd);
string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"" + lineEnd + lineEnd + "{1}" + lineEnd;
if (!string.IsNullOrEmpty(reqState.options.Params))
{
Dictionary<string, string> paramMap = parseHeaders(reqState.options.Params);
foreach (string key in paramMap.Keys)
{
requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
string formItem = string.Format(formdataTemplate, key, paramMap[key]);
byte[] formItemBytes = System.Text.Encoding.UTF8.GetBytes(formItem);
requestStream.Write(formItemBytes, 0, formItemBytes.Length);
}
}
using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!isoFile.FileExists(reqState.options.FilePath))
{
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(FileNotFoundError, reqState.options.Server, reqState.options.FilePath, 0)));
return;
}
byte[] endRequest = System.Text.Encoding.UTF8.GetBytes(lineEnd + lineStart + Boundary + lineStart + lineEnd);
long totalBytesToSend = 0;
using (FileStream fileStream = new IsolatedStorageFileStream(reqState.options.FilePath, FileMode.Open, isoFile))
{
string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"" + lineEnd + "Content-Type: {2}" + lineEnd + lineEnd;
string header = string.Format(headerTemplate, reqState.options.FileKey, reqState.options.FileName, reqState.options.MimeType);
byte[] headerBytes = System.Text.Encoding.UTF8.GetBytes(header);
byte[] buffer = new byte[4096];
int bytesRead = 0;
//sent bytes needs to be reseted before new upload
bytesSent = 0;
totalBytesToSend = fileStream.Length;
requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
requestStream.Write(headerBytes, 0, headerBytes.Length);
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
{
if (!reqState.isCancelled)
{
requestStream.Write(buffer, 0, bytesRead);
bytesSent += bytesRead;
DispatchFileTransferProgress(bytesSent, totalBytesToSend, callbackId);
System.Threading.Thread.Sleep(1);
}
else
{
throw new Exception("UploadCancelledException");
}
}
}
requestStream.Write(endRequest, 0, endRequest.Length);
}
}
// webRequest
webRequest.BeginGetResponse(ReadCallback, reqState);
}
catch (Exception /*ex*/)
{
if (!reqState.isCancelled)
{
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, new FileTransferError(ConnectionError)), callbackId);
}
}
}
/// <summary>
/// Reads response into FileUploadResult
/// </summary>
/// <param name="asynchronousResult"></param>
private void ReadCallback(IAsyncResult asynchronousResult)
{
DownloadRequestState reqState = (DownloadRequestState)asynchronousResult.AsyncState;
try
{
HttpWebRequest webRequest = reqState.request;
string callbackId = reqState.options.CallbackId;
if (InProcDownloads.ContainsKey(reqState.options.Id))
{
InProcDownloads.Remove(reqState.options.Id);
}
using (HttpWebResponse response = (HttpWebResponse)webRequest.EndGetResponse(asynchronousResult))
{
using (Stream streamResponse = response.GetResponseStream())
{
using (StreamReader streamReader = new StreamReader(streamResponse))
{
string responseString = streamReader.ReadToEnd();
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
DispatchCommandResult(new PluginResult(PluginResult.Status.OK, new FileUploadResult(bytesSent, (long)response.StatusCode, responseString)));
});
}
}
}
}
catch (WebException webex)
{
// TODO: probably need better work here to properly respond with all http status codes back to JS
// Right now am jumping through hoops just to detect 404.
if ((webex.Status == WebExceptionStatus.ProtocolError && ((HttpWebResponse)webex.Response).StatusCode == HttpStatusCode.NotFound)
|| webex.Status == WebExceptionStatus.UnknownError)
{
int statusCode = (int)((HttpWebResponse)webex.Response).StatusCode;
FileTransferError ftError = new FileTransferError(ConnectionError, null, null, statusCode);
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, ftError), reqState.options.CallbackId);
}
else
{
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR,
new FileTransferError(ConnectionError)),
reqState.options.CallbackId);
}
}
catch (Exception /*ex*/)
{
FileTransferError transferError = new FileTransferError(ConnectionError, reqState.options.Server, reqState.options.FilePath, 403);
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, transferError), reqState.options.CallbackId);
}
}
// Gets the full path without the filename
private string getDirectoryName(String filePath)
{
string directoryName;
try
{
directoryName = filePath.Substring(0, filePath.LastIndexOf('/'));
}
catch
{
directoryName = "";
}
return directoryName;
}
}
}

View File

@@ -0,0 +1,75 @@
#!/usr/bin/env node
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
var path = require('path');
var fs = require('fs');
module.exports = function(context) {
function main() {
// get the file transfer server address from the specified variables
var fileTransferServerAddress = getFileTransferServerAddress(context) || getDefaultFileTransferServerAddress(context);
console.log('Tests will use the following file transfer server address: ' + fileTransferServerAddress);
console.log('If you\'re using cordova@6.3.1 and the above address is wrong at "platform add", don\'t worry, it\'ll fix itself on "cordova run" or "cordova prepare".');
// pass it to the tests
writeFileTransferOptions(fileTransferServerAddress, context);
}
function getDefaultFileTransferServerAddress(context) {
var address = null;
var configNodes = context.opts.plugin.pluginInfo._et._root._children;
for (var node in configNodes) {
if (configNodes[node].attrib.name == 'FILETRANSFER_SERVER_ADDRESS') {
address = configNodes[node].attrib.default;
}
}
return address;
}
function getFileTransferServerAddress(context) {
var platformJsonFile = path.join(context.opts.projectRoot, 'platforms', context.opts.platforms[0], context.opts.platforms[0] + '.json');
var platformJson = JSON.parse(fs.readFileSync(platformJsonFile, 'utf8'));
if (platformJson && platformJson.installed_plugins && platformJson.installed_plugins['cordova-plugin-file-transfer-tests'] && platformJson.installed_plugins['cordova-plugin-file-transfer-tests'].FILETRANSFER_SERVER_ADDRESS) {
return platformJson.installed_plugins['cordova-plugin-file-transfer-tests'].FILETRANSFER_SERVER_ADDRESS;
} else {
return null;
}
}
function writeFileTransferOptions(address, context) {
for (var p in context.opts.paths) {
var ftOpts = {
serverAddress: address
};
var ftOptsString = JSON.stringify(ftOpts);
var ftOptsFile = path.join(context.opts.paths[p], 'fileTransferOpts.json');
fs.writeFileSync(ftOptsFile, ftOptsString, 'utf8');
}
}
main();
};

View File

@@ -0,0 +1,14 @@
{
"name": "cordova-plugin-file-transfer-tests",
"version": "1.6.3-dev",
"description": "",
"cordova": {
"id": "cordova-plugin-file-transfer-tests",
"platforms": []
},
"keywords": [
"ecosystem:cordova"
],
"author": "",
"license": "Apache 2.0"
}

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="cordova-plugin-file-transfer-tests"
version="1.7.1">
<name>Cordova File Transfer Plugin Tests</name>
<license>Apache 2.0</license>
<js-module src="tests.js" name="tests">
</js-module>
<hook type="after_prepare" src="hooks/after_prepare.js" />
<preference name="FILETRANSFER_SERVER_ADDRESS" default="http://rwswbpiopr.localtunnel.me"/>
</plugin>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,136 @@
// Type definitions for Apache Cordova FileTransfer plugin
// Project: https://github.com/apache/cordova-plugin-file-transfer
// Definitions by: Microsoft Open Technologies Inc. <http://msopentech.com>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
//
// Copyright (c) Microsoft Open Technologies Inc
// Licensed under the MIT license
/// <reference types="cordova-plugin-file" />
/**
* The FileTransfer object provides a way to upload files using an HTTP multi-part POST request,
* and to download files as well.
*/
interface FileTransfer {
/** Called with a ProgressEvent whenever a new chunk of data is transferred. */
onprogress: (event: ProgressEvent) => void;
/**
* Sends a file to a server.
* @param fileURL Filesystem URL representing the file on the device. For backwards compatibility,
* this can also be the full path of the file on the device.
* @param server URL of the server to receive the file, as encoded by encodeURI().
* @param successCallback A callback that is passed a FileUploadResult object.
* @param errorCallback A callback that executes if an error occurs retrieving the FileUploadResult.
* Invoked with a FileTransferError object.
* @param options Optional parameters.
* @param trustAllHosts Optional parameter, defaults to false. If set to true, it accepts all security certificates.
* This is useful since Android rejects self-signed security certificates.
* Not recommended for production use. Supported on Android and iOS.
*/
upload(
fileURL: string,
server: string,
successCallback: (result: FileUploadResult) => void,
errorCallback: (error: FileTransferError) => void,
options?: FileUploadOptions,
trustAllHosts?: boolean): void;
/**
* downloads a file from server.
* @param source URL of the server to download the file, as encoded by encodeURI().
* @param target Filesystem url representing the file on the device. For backwards compatibility,
* this can also be the full path of the file on the device.
* @param successCallback A callback that is passed a FileEntry object. (Function)
* @param errorCallback A callback that executes if an error occurs when retrieving the fileEntry.
* Invoked with a FileTransferError object.
* @param options Optional parameters.
* @param trustAllHosts Optional parameter, defaults to false. If set to true, it accepts all security certificates.
* This is useful since Android rejects self-signed security certificates.
* Not recommended for production use. Supported on Android and iOS.
*/
download(
source: string,
target: string,
successCallback: (fileEntry: FileEntry) => void,
errorCallback: (error: FileTransferError) => void,
trustAllHosts?: boolean,
options?: FileDownloadOptions): void;
/**
* Aborts an in-progress transfer. The onerror callback is passed a FileTransferError object
* which has an error code of FileTransferError.ABORT_ERR.
*/
abort(): void;
}
declare var FileTransfer: {
new (): FileTransfer;
};
/** A FileUploadResult object is passed to the success callback of the FileTransfer object's upload() method. */
interface FileUploadResult {
/** The number of bytes sent to the server as part of the upload. */
bytesSent: number;
/** The HTTP response code returned by the server. */
responseCode: number;
/** The HTTP response returned by the server. */
response: string;
/** The HTTP response headers by the server. Currently supported on iOS only.*/
headers: any;
}
/** Optional parameters for upload method. */
interface FileUploadOptions {
/** The name of the form element. Defaults to file. */
fileKey?: string;
/** The file name to use when saving the file on the server. Defaults to image.jpg. */
fileName?: string;
/** The HTTP method to use - either `PUT` or `POST`. Defaults to `POST`. */
httpMethod?: string;
/** The mime type of the data to upload. Defaults to image/jpeg. */
mimeType?: string;
/** A set of optional key/value pairs to pass in the HTTP request. */
params?: Object;
/** Whether to upload the data in chunked streaming mode. Defaults to true. */
chunkedMode?: boolean;
/** A map of header name/header values. Use an array to specify more than one value. */
headers?: Object;
}
/** Optional parameters for download method. */
interface FileDownloadOptions {
/** A map of header name/header values. */
headers?: {};
}
/** A FileTransferError object is passed to an error callback when an error occurs. */
interface FileTransferError {
/**
* One of the predefined error codes listed below.
* FileTransferError.FILE_NOT_FOUND_ERR
* FileTransferError.INVALID_URL_ERR
* FileTransferError.CONNECTION_ERR
* FileTransferError.ABORT_ERR
* FileTransferError.NOT_MODIFIED_ERR
*/
code: number;
/** URL to the source. */
source: string;
/** URL to the target. */
target: string;
/** HTTP status code. This attribute is only available when a response code is received from the HTTP connection. */
http_status: number;
/* Response body. This attribute is only available when a response is received from the HTTP connection. */
body: string;
/* Exception that is thrown by native code */
exception: any;
}
declare var FileTransferError: {
/** Constructor for FileTransferError object */
new (code?: number, source?: string, target?: string, status?: number, body?: any, exception?: any): FileTransferError;
FILE_NOT_FOUND_ERR: number;
INVALID_URL_ERR: number;
CONNECTION_ERR: number;
ABORT_ERR: number;
NOT_MODIFIED_ERR: number;
}

View File

@@ -0,0 +1,239 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
/* global cordova, FileSystem */
var argscheck = require('cordova/argscheck'),
exec = require('cordova/exec'),
FileTransferError = require('./FileTransferError'),
ProgressEvent = require('cordova-plugin-file.ProgressEvent');
function newProgressEvent(result) {
var pe = new ProgressEvent();
pe.lengthComputable = result.lengthComputable;
pe.loaded = result.loaded;
pe.total = result.total;
return pe;
}
function getUrlCredentials(urlString) {
var credentialsPattern = /^https?\:\/\/(?:(?:(([^:@\/]*)(?::([^@\/]*))?)?@)?([^:\/?#]*)(?::(\d*))?).*$/,
credentials = credentialsPattern.exec(urlString);
return credentials && credentials[1];
}
function getBasicAuthHeader(urlString) {
var header = null;
// This is changed due to MS Windows doesn't support credentials in http uris
// so we detect them by regexp and strip off from result url
// Proof: http://social.msdn.microsoft.com/Forums/windowsapps/en-US/a327cf3c-f033-4a54-8b7f-03c56ba3203f/windows-foundation-uri-security-problem
if (window.btoa) {
var credentials = getUrlCredentials(urlString);
if (credentials) {
var authHeader = "Authorization";
var authHeaderValue = "Basic " + window.btoa(credentials);
header = {
name : authHeader,
value : authHeaderValue
};
}
}
return header;
}
function convertHeadersToArray(headers) {
var result = [];
for (var header in headers) {
if (headers.hasOwnProperty(header)) {
var headerValue = headers[header];
result.push({
name: header,
value: headerValue.toString()
});
}
}
return result;
}
var idCounter = 0;
/**
* FileTransfer uploads a file to a remote server.
* @constructor
*/
var FileTransfer = function() {
this._id = ++idCounter;
this.onprogress = null; // optional callback
};
/**
* Given an absolute file path, uploads a file on the device to a remote server
* using a multipart HTTP request.
* @param filePath {String} Full path of the file on the device
* @param server {String} URL of the server to receive the file
* @param successCallback (Function} Callback to be invoked when upload has completed
* @param errorCallback {Function} Callback to be invoked upon error
* @param options {FileUploadOptions} Optional parameters such as file name and mimetype
* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
*/
FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) {
argscheck.checkArgs('ssFFO*', 'FileTransfer.upload', arguments);
// check for options
var fileKey = null;
var fileName = null;
var mimeType = null;
var params = null;
var chunkedMode = true;
var headers = null;
var httpMethod = null;
var basicAuthHeader = getBasicAuthHeader(server);
if (basicAuthHeader) {
server = server.replace(getUrlCredentials(server) + '@', '');
options = options || {};
options.headers = options.headers || {};
options.headers[basicAuthHeader.name] = basicAuthHeader.value;
}
if (options) {
fileKey = options.fileKey;
fileName = options.fileName;
mimeType = options.mimeType;
headers = options.headers;
httpMethod = options.httpMethod || "POST";
if (httpMethod.toUpperCase() == "PUT"){
httpMethod = "PUT";
} else {
httpMethod = "POST";
}
if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") {
chunkedMode = options.chunkedMode;
}
if (options.params) {
params = options.params;
}
else {
params = {};
}
}
if (cordova.platformId === "windowsphone") {
headers = headers && convertHeadersToArray(headers);
params = params && convertHeadersToArray(params);
}
var fail = errorCallback && function(e) {
var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body, e.exception);
errorCallback(error);
};
var self = this;
var win = function(result) {
if (typeof result.lengthComputable != "undefined") {
if (self.onprogress) {
self.onprogress(newProgressEvent(result));
}
} else {
if (successCallback) {
successCallback(result);
}
}
};
exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id, httpMethod]);
};
/**
* Downloads a file form a given URL and saves it to the specified directory.
* @param source {String} URL of the server to receive the file
* @param target {String} Full path of the file on the device
* @param successCallback (Function} Callback to be invoked when upload has completed
* @param errorCallback {Function} Callback to be invoked upon error
* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
* @param options {FileDownloadOptions} Optional parameters such as headers
*/
FileTransfer.prototype.download = function(source, target, successCallback, errorCallback, trustAllHosts, options) {
argscheck.checkArgs('ssFF*', 'FileTransfer.download', arguments);
var self = this;
var basicAuthHeader = getBasicAuthHeader(source);
if (basicAuthHeader) {
source = source.replace(getUrlCredentials(source) + '@', '');
options = options || {};
options.headers = options.headers || {};
options.headers[basicAuthHeader.name] = basicAuthHeader.value;
}
var headers = null;
if (options) {
headers = options.headers || null;
}
if (cordova.platformId === "windowsphone" && headers) {
headers = convertHeadersToArray(headers);
}
var win = function(result) {
if (typeof result.lengthComputable != "undefined") {
if (self.onprogress) {
return self.onprogress(newProgressEvent(result));
}
} else if (successCallback) {
var entry = null;
if (result.isDirectory) {
entry = new (require('cordova-plugin-file.DirectoryEntry'))();
}
else if (result.isFile) {
entry = new (require('cordova-plugin-file.FileEntry'))();
}
entry.isDirectory = result.isDirectory;
entry.isFile = result.isFile;
entry.name = result.name;
entry.fullPath = result.fullPath;
entry.filesystem = new FileSystem(result.filesystemName || (result.filesystem == window.PERSISTENT ? 'persistent' : 'temporary'));
entry.nativeURL = result.nativeURL;
successCallback(entry);
}
};
var fail = errorCallback && function(e) {
var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body, e.exception);
errorCallback(error);
};
exec(win, fail, 'FileTransfer', 'download', [source, target, trustAllHosts, this._id, headers]);
};
/**
* Aborts the ongoing file transfer on this object. The original error
* callback for the file transfer will be called if necessary.
*/
FileTransfer.prototype.abort = function() {
exec(null, null, 'FileTransfer', 'abort', [this._id]);
};
module.exports = FileTransfer;

View File

@@ -0,0 +1,41 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
/**
* FileTransferError
* @constructor
*/
var FileTransferError = function(code, source, target, status, body, exception) {
this.code = code || null;
this.source = source || null;
this.target = target || null;
this.http_status = status || null;
this.body = body || null;
this.exception = exception || null;
};
FileTransferError.FILE_NOT_FOUND_ERR = 1;
FileTransferError.INVALID_URL_ERR = 2;
FileTransferError.CONNECTION_ERR = 3;
FileTransferError.ABORT_ERR = 4;
FileTransferError.NOT_MODIFIED_ERR = 5;
module.exports = FileTransferError;

View File

@@ -0,0 +1,5 @@
{
"globals": {
"requestAnimationFrame": true
}
}

View File

@@ -0,0 +1,190 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
var argscheck = require('cordova/argscheck'),
FileTransferError = require('./FileTransferError'),
xhrImpl = require('./BB10XHRImplementation');
function getBasicAuthHeader(urlString) {
var header = null;
if (window.btoa) {
// parse the url using the Location object
var url = document.createElement('a');
url.href = urlString;
var credentials = null;
var protocol = url.protocol + "//";
var origin = protocol + url.host;
// check whether there are the username:password credentials in the url
if (url.href.indexOf(origin) !== 0) { // credentials found
var atIndex = url.href.indexOf("@");
credentials = url.href.substring(protocol.length, atIndex);
}
if (credentials) {
var authHeader = "Authorization";
var authHeaderValue = "Basic " + window.btoa(credentials);
header = {
name : authHeader,
value : authHeaderValue
};
}
}
return header;
}
var idCounter = 0;
/**
* FileTransfer uploads a file to a remote server.
* @constructor
*/
var FileTransfer = function() {
this._id = ++idCounter;
this.onprogress = null; // optional callback
};
/**
* Given an absolute file path, uploads a file on the device to a remote server
* using a multipart HTTP request.
* @param filePath {String} Full path of the file on the device
* @param server {String} URL of the server to receive the file
* @param successCallback (Function} Callback to be invoked when upload has completed
* @param errorCallback {Function} Callback to be invoked upon error
* @param options {FileUploadOptions} Optional parameters such as file name and mimetype
* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
*/
FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) {
argscheck.checkArgs('ssFFO*', 'FileTransfer.upload', arguments);
// check for options
var fileKey = null;
var fileName = null;
var mimeType = null;
var params = null;
var chunkedMode = true;
var headers = null;
var httpMethod = null;
var basicAuthHeader = getBasicAuthHeader(server);
if (basicAuthHeader) {
options = options || {};
options.headers = options.headers || {};
options.headers[basicAuthHeader.name] = basicAuthHeader.value;
}
if (options) {
fileKey = options.fileKey;
fileName = options.fileName;
mimeType = options.mimeType;
headers = options.headers;
httpMethod = options.httpMethod || "POST";
if (httpMethod.toUpperCase() == "PUT"){
httpMethod = "PUT";
} else {
httpMethod = "POST";
}
if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") {
chunkedMode = options.chunkedMode;
}
if (options.params) {
params = options.params;
}
else {
params = {};
}
}
var fail = errorCallback && function(e) {
var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body);
errorCallback(error);
};
var self = this;
var win = function(result) {
if (typeof result.lengthComputable != "undefined") {
if (self.onprogress) {
self.onprogress(result);
}
} else {
if (successCallback) {
successCallback(result);
}
}
};
xhrImpl.upload(win, fail, [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id, httpMethod]);
};
/**
* Downloads a file form a given URL and saves it to the specified directory.
* @param source {String} URL of the server to receive the file
* @param target {String} Full path of the file on the device
* @param successCallback (Function} Callback to be invoked when upload has completed
* @param errorCallback {Function} Callback to be invoked upon error
* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
* @param options {FileDownloadOptions} Optional parameters such as headers
*/
FileTransfer.prototype.download = function(source, target, successCallback, errorCallback, trustAllHosts, options) {
argscheck.checkArgs('ssFF*', 'FileTransfer.download', arguments);
var self = this;
var basicAuthHeader = getBasicAuthHeader(source);
if (basicAuthHeader) {
options = options || {};
options.headers = options.headers || {};
options.headers[basicAuthHeader.name] = basicAuthHeader.value;
}
var headers = null;
if (options) {
headers = options.headers || null;
}
var win = function(result) {
if (typeof result.lengthComputable != "undefined") {
if (self.onprogress) {
return self.onprogress(result);
}
} else if (successCallback) {
successCallback(result);
}
};
var fail = errorCallback && function(e) {
var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body);
errorCallback(error);
};
xhrImpl.download(win, fail, [source, target, trustAllHosts, this._id, headers]);
};
/**
* Aborts the ongoing file transfer on this object. The original error
* callback for the file transfer will be called if necessary.
*/
FileTransfer.prototype.abort = function() {
xhrImpl.abort(null, null, [this._id]);
};
module.exports = FileTransfer;

View File

@@ -0,0 +1,36 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
/*
* FileTransferProxy
*
* Register all FileTransfer exec calls to be handled by proxy
*/
var xhrFileTransfer = require('cordova-plugin-file-transfer.xhrFileTransfer');
module.exports = {
abort: xhrFileTransfer.abort,
download: xhrFileTransfer.download,
upload: xhrFileTransfer.upload
};
require('cordova/exec/proxy').add('FileTransfer', module.exports);

View File

@@ -0,0 +1,260 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
/* global Blob:false */
var cordova = require('cordova'),
resolve = cordova.require('cordova-plugin-file.resolveLocalFileSystemURIProxy'),
requestAnimationFrame = cordova.require('cordova-plugin-file.bb10RequestAnimationFrame'),
xhr = {};
function getParentPath(filePath) {
var pos = filePath.lastIndexOf('/');
return filePath.substring(0, pos + 1);
}
function getFileName(filePath) {
var pos = filePath.lastIndexOf('/');
return filePath.substring(pos + 1);
}
function checkURL(url) {
return url.indexOf(' ') === -1 ? true : false;
}
module.exports = {
abort: function (win, fail, args) {
var id = args[0];
if (xhr[id]) {
xhr[id].abort();
if (typeof(win) === 'function') {
win();
}
} else if (typeof(fail) === 'function') {
fail();
}
},
upload: function(win, fail, args) {
var filePath = args[0],
server = args[1],
fileKey = args[2],
fileName = args[3],
mimeType = args[4],
params = args[5],
/*trustAllHosts = args[6],*/
chunkedMode = args[7],
headers = args[8],
onSuccess = function (data) {
if (typeof(win) === 'function') {
win(data);
}
},
onFail = function (code) {
delete xhr[fileKey];
if (typeof(fail) === 'function') {
fail(code);
}
};
if (!checkURL(server)) {
onFail(new FileTransferError(FileTransferError.INVALID_URL_ERR, server, filePath));
}
xhr[fileKey] = new XMLHttpRequest();
xhr[fileKey].onabort = function () {
onFail(new FileTransferError(FileTransferError.ABORT_ERR, server, filePath, this.status, xhr[fileKey].response));
};
resolve(function(entry) {
requestAnimationFrame(function () {
entry.nativeEntry.file(function(file) {
function uploadFile(blobFile) {
var fd = new FormData();
fd.append(fileKey, blobFile, fileName);
for (var prop in params) {
if(params.hasOwnProperty(prop)) {
fd.append(prop, params[prop]);
}
}
xhr[fileKey].open("POST", server);
xhr[fileKey].onload = function(evt) {
if (xhr[fileKey].status === 200) {
var result = new FileUploadResult();
result.bytesSent = file.size;
result.responseCode = xhr[fileKey].status;
result.response = xhr[fileKey].response;
delete xhr[fileKey];
onSuccess(result);
} else if (xhr[fileKey].status === 404) {
onFail(new FileTransferError(FileTransferError.INVALID_URL_ERR, server, filePath, xhr[fileKey].status, xhr[fileKey].response));
} else {
onFail(new FileTransferError(FileTransferError.CONNECTION_ERR, server, filePath, xhr[fileKey].status, xhr[fileKey].response));
}
};
xhr[fileKey].ontimeout = function(evt) {
onFail(new FileTransferError(FileTransferError.CONNECTION_ERR, server, filePath, xhr[fileKey].status, xhr[fileKey].response));
};
xhr[fileKey].onerror = function () {
onFail(new FileTransferError(FileTransferError.CONNECTION_ERR, server, filePath, this.status, xhr[fileKey].response));
};
xhr[fileKey].upload.onprogress = function (evt) {
if (evt.loaded > 0) {
onSuccess(evt);
}
};
for (var header in headers) {
if (headers.hasOwnProperty(header)) {
xhr[fileKey].setRequestHeader(header, headers[header]);
}
}
requestAnimationFrame(function () {
xhr[fileKey].send(fd);
});
}
var bytesPerChunk;
if (chunkedMode === true) {
bytesPerChunk = 1024 * 1024; // 1MB chunk sizes.
} else {
bytesPerChunk = file.size;
}
var start = 0;
var end = bytesPerChunk;
while (start < file.size) {
var chunk = file.slice(start, end, mimeType);
uploadFile(chunk);
start = end;
end = start + bytesPerChunk;
}
}, function(error) {
onFail(new FileTransferError(FileTransferError.FILE_NOT_FOUND_ERR, server, filePath));
});
});
}, function(error) {
onFail(new FileTransferError(FileTransferError.FILE_NOT_FOUND_ERR, server, filePath));
}, [filePath]);
},
download: function (win, fail, args) {
var source = args[0],
target = args[1],
id = args[3],
headers = args[4],
fileWriter,
onSuccess = function (entry) {
if (typeof(win) === 'function') {
win(entry);
}
},
onFail = function (error) {
var reader;
delete xhr[id];
if (typeof(fail) === 'function') {
if (error && error.body && typeof(error.body) === 'object') {
reader = new FileReader()._realReader;
reader.onloadend = function () {
error.body = this.result;
fail(error);
};
reader.onerror = function () {
fail(error);
};
reader.readAsText(error.body);
} else {
fail(error);
}
}
};
if (!checkURL(source)) {
onFail(new FileTransferError(FileTransferError.INVALID_URL_ERR, source, target));
}
xhr[id] = new XMLHttpRequest();
function writeFile(entry) {
entry.createWriter(function (writer) {
fileWriter = writer;
fileWriter.onwriteend = function (evt) {
if (!evt.target.error) {
entry.filesystemName = entry.filesystem.name;
delete xhr[id];
onSuccess(entry);
} else {
onFail(evt.target.error);
}
};
fileWriter.onerror = function (evt) {
onFail(evt.target.error);
};
fileWriter.write(new Blob([xhr[id].response]));
}, function (error) {
onFail(error);
});
}
xhr[id].onerror = function (e) {
onFail(new FileTransferError(FileTransferError.CONNECTION_ERR, source, target, xhr[id].status, xhr[id].response));
};
xhr[id].onabort = function (e) {
onFail(new FileTransferError(FileTransferError.ABORT_ERR, source, target, xhr[id].status, xhr[id].response));
};
xhr[id].onload = function () {
if (xhr[id].readyState === xhr[id].DONE) {
if (xhr[id].status === 200 && xhr[id].response) {
resolveLocalFileSystemURI(getParentPath(target), function (dir) {
dir.getFile(getFileName(target), {create: true}, writeFile, function (error) {
onFail(new FileTransferError(FileTransferError.FILE_NOT_FOUND_ERR, source, target, xhr[id].status, xhr[id].response));
});
}, function (error) {
onFail(new FileTransferError(FileTransferError.FILE_NOT_FOUND_ERR, source, target, xhr[id].status, xhr[id].response));
});
} else if (xhr[id].status === 404) {
onFail(new FileTransferError(FileTransferError.INVALID_URL_ERR, source, target, xhr[id].status, xhr[id].response));
} else {
onFail(new FileTransferError(FileTransferError.CONNECTION_ERR, source, target, xhr[id].status, xhr[id].response));
}
}
};
xhr[id].onprogress = function (evt) {
onSuccess(evt);
};
xhr[id].open("GET", source, true);
for (var header in headers) {
if (headers.hasOwnProperty(header)) {
xhr[id].setRequestHeader(header, headers[header]);
}
}
xhr[id].responseType = "blob";
requestAnimationFrame(function () {
if (xhr[id]) {
xhr[id].send();
}
});
}
};

View File

@@ -0,0 +1,346 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
/*global module, require*/
var argscheck = require('cordova/argscheck'),
FileTransferError = require('./FileTransferError');
function getParentPath(filePath) {
var pos = filePath.lastIndexOf('/');
return filePath.substring(0, pos + 1);
}
function getFileName(filePath) {
var pos = filePath.lastIndexOf('/');
return filePath.substring(pos + 1);
}
function getUrlCredentials(urlString) {
var credentialsPattern = /^https?\:\/\/(?:(?:(([^:@\/]*)(?::([^@\/]*))?)?@)?([^:\/?#]*)(?::(\d*))?).*$/,
credentials = credentialsPattern.exec(urlString);
return credentials && credentials[1];
}
function getBasicAuthHeader(urlString) {
var header = null;
// This is changed due to MS Windows doesn't support credentials in http uris
// so we detect them by regexp and strip off from result url
// Proof: http://social.msdn.microsoft.com/Forums/windowsapps/en-US/a327cf3c-f033-4a54-8b7f-03c56ba3203f/windows-foundation-uri-security-problem
if (window.btoa) {
var credentials = getUrlCredentials(urlString);
if (credentials) {
var authHeader = "Authorization";
var authHeaderValue = "Basic " + window.btoa(credentials);
header = {
name : authHeader,
value : authHeaderValue
};
}
}
return header;
}
function checkURL(url) {
return url.indexOf(' ') === -1 ? true : false;
}
var idCounter = 0;
var transfers = {};
/**
* FileTransfer uploads a file to a remote server.
* @constructor
*/
var FileTransfer = function() {
this._id = ++idCounter;
this.onprogress = null; // optional callback
};
/**
* Given an absolute file path, uploads a file on the device to a remote server
* using a multipart HTTP request.
* @param filePath {String} Full path of the file on the device
* @param server {String} URL of the server to receive the file
* @param successCallback (Function} Callback to be invoked when upload has completed
* @param errorCallback {Function} Callback to be invoked upon error
* @param options {FileUploadOptions} Optional parameters such as file name and mimetype
* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
*/
FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options) {
// check for arguments
argscheck.checkArgs('ssFFO*', 'FileTransfer.upload', arguments);
// Check if target URL doesn't contain spaces. If contains, it should be escaped first
// (see https://github.com/apache/cordova-plugin-file-transfer/blob/master/doc/index.md#upload)
if (!checkURL(server)) {
if (errorCallback) {
errorCallback(new FileTransferError(FileTransferError.INVALID_URL_ERR, filePath, server));
}
return;
}
options = options || {};
var fileKey = options.fileKey || "file";
var fileName = options.fileName || "image.jpg";
var mimeType = options.mimeType || "image/jpeg";
var params = options.params || {};
var withCredentials = options.withCredentials || false;
// var chunkedMode = !!options.chunkedMode; // Not supported
var headers = options.headers || {};
var httpMethod = options.httpMethod && options.httpMethod.toUpperCase() === "PUT" ? "PUT" : "POST";
var basicAuthHeader = getBasicAuthHeader(server);
if (basicAuthHeader) {
server = server.replace(getUrlCredentials(server) + '@', '');
headers[basicAuthHeader.name] = basicAuthHeader.value;
}
var that = this;
var xhr = transfers[this._id] = new XMLHttpRequest();
xhr.withCredentials = withCredentials;
var fail = errorCallback && function(code, status, response) {
if (transfers[this._id]) {
delete transfers[this._id];
}
var error = new FileTransferError(code, filePath, server, status, response);
if (errorCallback) {
errorCallback(error);
}
};
window.resolveLocalFileSystemURL(filePath, function(entry) {
entry.file(function(file) {
var reader = new FileReader();
reader.onloadend = function() {
var blob = new Blob([this.result], {type: mimeType});
// Prepare form data to send to server
var fd = new FormData();
fd.append(fileKey, blob, fileName);
for (var prop in params) {
if (params.hasOwnProperty(prop)) {
fd.append(prop, params[prop]);
}
}
xhr.open(httpMethod, server);
// Fill XHR headers
for (var header in headers) {
if (headers.hasOwnProperty(header)) {
xhr.setRequestHeader(header, headers[header]);
}
}
xhr.onload = function() {
// 2xx codes are valid
if (this.status >= 200 &&
this.status < 300) {
var result = new FileUploadResult(); // jshint ignore:line
result.bytesSent = blob.size;
result.responseCode = this.status;
result.response = this.response;
delete transfers[that._id];
successCallback(result);
} else if (this.status === 404) {
fail(FileTransferError.INVALID_URL_ERR, this.status, this.response);
} else {
fail(FileTransferError.CONNECTION_ERR, this.status, this.response);
}
};
xhr.ontimeout = function() {
fail(FileTransferError.CONNECTION_ERR, this.status, this.response);
};
xhr.onerror = function() {
fail(FileTransferError.CONNECTION_ERR, this.status, this.response);
};
xhr.onabort = function () {
fail(FileTransferError.ABORT_ERR, this.status, this.response);
};
xhr.upload.onprogress = function (e) {
if (that.onprogress) {
that.onprogress(e);
}
};
xhr.send(fd);
// Special case when transfer already aborted, but XHR isn't sent.
// In this case XHR won't fire an abort event, so we need to check if transfers record
// isn't deleted by filetransfer.abort and if so, call XHR's abort method again
if (!transfers[that._id]) {
xhr.abort();
}
};
reader.readAsArrayBuffer(file);
}, function() {
fail(FileTransferError.FILE_NOT_FOUND_ERR);
});
}, function() {
fail(FileTransferError.FILE_NOT_FOUND_ERR);
});
};
/**
* Downloads a file form a given URL and saves it to the specified directory.
* @param source {String} URL of the server to receive the file
* @param target {String} Full path of the file on the device
* @param successCallback (Function} Callback to be invoked when upload has completed
* @param errorCallback {Function} Callback to be invoked upon error
* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
* @param options {FileDownloadOptions} Optional parameters such as headers
*/
FileTransfer.prototype.download = function(source, target, successCallback, errorCallback, trustAllHosts, options) {
argscheck.checkArgs('ssFF*', 'FileTransfer.download', arguments);
// Check if target URL doesn't contain spaces. If contains, it should be escaped first
// (see https://github.com/apache/cordova-plugin-file-transfer/blob/master/doc/index.md#download)
if (!checkURL(source)) {
if (errorCallback) {
errorCallback(new FileTransferError(FileTransferError.INVALID_URL_ERR, source, target));
}
return;
}
options = options || {};
var headers = options.headers || {};
var withCredentials = options.withCredentials || false;
var basicAuthHeader = getBasicAuthHeader(source);
if (basicAuthHeader) {
source = source.replace(getUrlCredentials(source) + '@', '');
headers[basicAuthHeader.name] = basicAuthHeader.value;
}
var that = this;
var xhr = transfers[this._id] = new XMLHttpRequest();
xhr.withCredentials = withCredentials;
var fail = errorCallback && function(code, status, response) {
if (transfers[that._id]) {
delete transfers[that._id];
}
// In XHR GET reqests we're setting response type to Blob
// but in case of error we need to raise event with plain text response
if (response instanceof Blob) {
var reader = new FileReader();
reader.readAsText(response);
reader.onloadend = function(e) {
var error = new FileTransferError(code, source, target, status, e.target.result);
errorCallback(error);
};
} else {
var error = new FileTransferError(code, source, target, status, response);
errorCallback(error);
}
};
xhr.onload = function (e) {
var fileNotFound = function () {
fail(FileTransferError.FILE_NOT_FOUND_ERR);
};
var req = e.target;
// req.status === 0 is special case for local files with file:// URI scheme
if ((req.status === 200 || req.status === 0) && req.response) {
window.resolveLocalFileSystemURL(getParentPath(target), function (dir) {
dir.getFile(getFileName(target), {create: true}, function writeFile(entry) {
entry.createWriter(function (fileWriter) {
fileWriter.onwriteend = function (evt) {
if (!evt.target.error) {
entry.filesystemName = entry.filesystem.name;
delete transfers[that._id];
if (successCallback) {
successCallback(entry);
}
} else {
fail(FileTransferError.FILE_NOT_FOUND_ERR);
}
};
fileWriter.onerror = function () {
fail(FileTransferError.FILE_NOT_FOUND_ERR);
};
fileWriter.write(req.response);
}, fileNotFound);
}, fileNotFound);
}, fileNotFound);
} else if (req.status === 404) {
fail(FileTransferError.INVALID_URL_ERR, req.status, req.response);
} else {
fail(FileTransferError.CONNECTION_ERR, req.status, req.response);
}
};
xhr.onprogress = function (e) {
if (that.onprogress) {
that.onprogress(e);
}
};
xhr.onerror = function () {
fail(FileTransferError.CONNECTION_ERR, this.status, this.response);
};
xhr.onabort = function () {
fail(FileTransferError.ABORT_ERR, this.status, this.response);
};
xhr.open("GET", source, true);
for (var header in headers) {
if (headers.hasOwnProperty(header)) {
xhr.setRequestHeader(header, headers[header]);
}
}
xhr.responseType = "blob";
xhr.send();
};
/**
* Aborts the ongoing file transfer on this object. The original error
* callback for the file transfer will be called if necessary.
*/
FileTransfer.prototype.abort = function() {
if (this instanceof FileTransfer) {
if (transfers[this._id]) {
transfers[this._id].abort();
delete transfers[this._id];
}
}
};
module.exports = FileTransfer;

View File

@@ -0,0 +1,222 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
var FileTransferError = require('./FileTransferError'),
xhr = {};
function getParentPath(filePath) {
var pos = filePath.lastIndexOf('/');
return filePath.substring(0, pos + 1);
}
function getFileName(filePath) {
var pos = filePath.lastIndexOf('/');
return filePath.substring(pos + 1);
}
module.exports = {
abort: function (successCallback, errorCallback, args) {
var id = args[0];
if (xhr[id]) {
xhr[id].abort();
if (typeof(successCallback) === 'function') {
successCallback();
}
} else if (typeof(errorCallback) === 'function') {
errorCallback();
}
},
upload: function(successCallback, errorCallback, args) {
var filePath = args[0],
server = args[1],
fileKey = args[2],
fileName = args[3],
mimeType = args[4],
params = args[5],
/*trustAllHosts = args[6],*/
/*chunkedMode = args[7],*/
headers = args[8];
xhr[fileKey] = new XMLHttpRequest({mozSystem: true});
xhr[fileKey].onabort = function() {
onFail(new FileTransferError(FileTransferError.ABORT_ERR, server, filePath, this.status, xhr[fileKey].response));
};
window.resolveLocalFileSystemURL(filePath, function(entry) {
entry.file(function(file) {
var reader = new FileReader();
reader.onloadend = function() {
var blob = new Blob([this.result], {type: mimeType});
var fd = new FormData();
fd.append(fileKey, blob, fileName);
for (var prop in params) {
if (params.hasOwnProperty(prop)) {
fd.append(prop, params[prop]);
}
}
xhr[fileKey].open("POST", server);
xhr[fileKey].onload = function(evt) {
if (xhr[fileKey].status === 200) {
var result = new FileUploadResult();
result.bytesSent = blob.size;
result.responseCode = xhr[fileKey].status;
result.response = xhr[fileKey].response;
delete xhr[fileKey];
onSuccess(result);
} else if (xhr[fileKey].status === 404) {
onFail(new FileTransferError(FileTransferError.INVALID_URL_ERR, server, filePath, xhr[fileKey].status, xhr[fileKey].response));
} else {
onFail(new FileTransferError(FileTransferError.CONNECTION_ERR, server, filePath, xhr[fileKey].status, xhr[fileKey].response));
}
};
xhr[fileKey].ontimeout = function() {
onFail(new FileTransferError(FileTransferError.CONNECTION_ERR, server, filePath, xhr[fileKey].status, xhr[fileKey].response));
};
xhr[fileKey].onerror = function() {
onFail(new FileTransferError(FileTransferError.CONNECTION_ERR, server, filePath, this.status, xhr[fileKey].response));
};
for (var header in headers) {
if (headers.hasOwnProperty(header)) {
xhr[fileKey].setRequestHeader(header, headers[header]);
}
}
xhr[fileKey].send(fd);
};
reader.readAsArrayBuffer(file);
}, function() {
onFail(new FileTransferError(FileTransferError.FILE_NOT_FOUND_ERR, server, filePath));
});
}, function() {
onFail(new FileTransferError(FileTransferError.FILE_NOT_FOUND_ERR, server, filePath));
});
function onSuccess(data) {
if (typeof(successCallback) === 'function') {
successCallback(data);
}
}
function onFail(code) {
delete xhr[fileKey];
if (typeof(errorCallback) === 'function') {
errorCallback(code);
}
}
},
download: function (successCallback, errorCallback, args) {
var source = args[0],
target = args[1],
id = args[3],
headers = args[4];
xhr[id] = new XMLHttpRequest({mozSystem: true});
xhr[id].onload = function () {
if (xhr[id].readyState === xhr[id].DONE) {
if (xhr[id].status === 200 && xhr[id].response) {
window.resolveLocalFileSystemURL(getParentPath(target), function (dir) {
dir.getFile(getFileName(target), {create: true}, writeFile, function (error) {
onFail(new FileTransferError(FileTransferError.FILE_NOT_FOUND_ERR, source, target, xhr[id].status, xhr[id].response));
});
}, function () {
onFail(new FileTransferError(FileTransferError.FILE_NOT_FOUND_ERR, source, target, xhr[id].status, xhr[id].response));
});
} else if (xhr[id].status === 404) {
onFail(new FileTransferError(FileTransferError.INVALID_URL_ERR, source, target, xhr[id].status, xhr[id].response));
} else {
onFail(new FileTransferError(FileTransferError.CONNECTION_ERR, source, target, xhr[id].status, xhr[id].response));
}
}
};
function writeFile(entry) {
entry.createWriter(function (fileWriter) {
fileWriter.onwriteend = function (evt) {
if (!evt.target.error) {
entry.filesystemName = entry.filesystem.name;
delete xhr[id];
onSuccess(entry);
} else {
onFail(evt.target.error);
}
};
fileWriter.onerror = function (evt) {
onFail(evt.target.error);
};
fileWriter.write(new Blob([xhr[id].response]));
}, function (error) {
onFail(error);
});
}
xhr[id].onerror = function (e) {
onFail(new FileTransferError(FileTransferError.CONNECTION_ERR, source, target, xhr[id].status, xhr[id].response));
};
xhr[id].onabort = function (e) {
onFail(new FileTransferError(FileTransferError.ABORT_ERR, source, target, xhr[id].status, xhr[id].response));
};
xhr[id].open("GET", source, true);
for (var header in headers) {
if (headers.hasOwnProperty(header)) {
xhr[id].setRequestHeader(header, headers[header]);
}
}
xhr[id].responseType = "blob";
setTimeout(function () {
if (xhr[id]) {
xhr[id].send();
}
}, 0);
function onSuccess(entry) {
if (typeof(successCallback) === 'function') {
successCallback(entry);
}
}
function onFail(error) {
delete xhr[id];
if (typeof(errorCallback) === 'function') {
errorCallback(error);
}
}
}
};
require('cordova/exec/proxy').add('FileTransfer', module.exports);

View File

@@ -0,0 +1,73 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
// jshint ignore: start
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
INVALID_CHARACTER_ERR = (function () {
// fabricate a suitable error object
try { document.createElement('$'); }
catch (error) { return error; }
}());
// encoder
// [https://gist.github.com/999166] by [https://github.com/nignag]
window.btoa || (
window.btoa = function (input) {
for (
// initialize result and counter
var block, charCode, idx = 0, map = chars, output = '';
// if the next input index does not exist:
// change the mapping table to "="
// check if d has no fractional digits
input.charAt(idx | 0) || (map = '=', idx % 1) ;
// "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8
output += map.charAt(63 & block >> 8 - idx % 1 * 8)
) {
charCode = input.charCodeAt(idx += 3 / 4);
if (charCode > 0xFF) throw INVALID_CHARACTER_ERR;
block = block << 8 | charCode;
}
return output;
});
// decoder
// [https://gist.github.com/1020396] by [https://github.com/atk]
window.atob || (
window.atob = function (input) {
input = input.replace(/=+$/, '')
if (input.length % 4 == 1) throw INVALID_CHARACTER_ERR;
for (
// initialize result and counters
var bc = 0, bs, buffer, idx = 0, output = '';
// get next character
buffer = input.charAt(idx++) ;
// character found in table? initialize bit storage and add its ascii value;
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
// and if not first of each 4 characters,
// convert the first 8 bits to one ascii character
bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
) {
// try to find character in table (0-63, not found => -1)
buffer = chars.indexOf(buffer);
}
return output;
});

View File

@@ -0,0 +1,37 @@
<!--
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
-->
# Contributing to Apache Cordova
Anyone can contribute to Cordova. And we need your contributions.
There are multiple ways to contribute: report bugs, improve the docs, and
contribute code.
For instructions on this, start with the
[contribution overview](http://cordova.apache.org/contribute/).
The details are explained there, but the important items are:
- Check for Github issues that corresponds to your contribution and link or create them if necessary.
- Run the tests so your patch doesn't break existing functionality.
We look forward to your contributions!

View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,5 @@
Apache Cordova
Copyright 2012 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).

View File

@@ -0,0 +1,848 @@
---
title: File
description: Read/write files on the device.
---
<!--
# license: Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-->
# cordova-plugin-file
[![Android Testsuite](https://github.com/apache/cordova-plugin-file/actions/workflows/android.yml/badge.svg)](https://github.com/apache/cordova-plugin-file/actions/workflows/android.yml) [![Chrome Testsuite](https://github.com/apache/cordova-plugin-file/actions/workflows/chrome.yml/badge.svg)](https://github.com/apache/cordova-plugin-file/actions/workflows/chrome.yml) [![iOS Testsuite](https://github.com/apache/cordova-plugin-file/actions/workflows/ios.yml/badge.svg)](https://github.com/apache/cordova-plugin-file/actions/workflows/ios.yml) [![Lint Test](https://github.com/apache/cordova-plugin-file/actions/workflows/lint.yml/badge.svg)](https://github.com/apache/cordova-plugin-file/actions/workflows/lint.yml)
This plugin implements a File API allowing read/write access to files residing on the device.
This plugin is based on several specs, including :
The HTML5 File API
[http://www.w3.org/TR/FileAPI/](http://www.w3.org/TR/FileAPI/)
The Directories and System extensions
Latest:
[http://www.w3.org/TR/2012/WD-file-system-api-20120417/](http://www.w3.org/TR/2012/WD-file-system-api-20120417/)
Although most of the plugin code was written when an earlier spec was current:
[http://www.w3.org/TR/2011/WD-file-system-api-20110419/](http://www.w3.org/TR/2011/WD-file-system-api-20110419/)
It also implements the FileWriter spec :
[http://dev.w3.org/2009/dap/file-system/file-writer.html](http://dev.w3.org/2009/dap/file-system/file-writer.html)
>*Note* While the W3C FileSystem spec is deprecated for web browsers, the FileSystem APIs are supported in Cordova applications with this plugin for the platforms listed in the _Supported Platforms_ list, with the exception of the Browser platform.
To get a few ideas how to use the plugin, check out the [sample](#sample) at the bottom of this page. For additional examples (browser focused), see the HTML5 Rocks' [FileSystem article.](http://www.html5rocks.com/en/tutorials/file/filesystem/)
For an overview of other storage options, refer to Cordova's
[storage guide](http://cordova.apache.org/docs/en/latest/cordova/storage/storage.html).
This plugin defines a global `cordova.file` object.
Although the object is in the global scope, it is not available to applications until after the `deviceready` event fires.
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
console.log(cordova.file);
}
## Installation
cordova plugin add cordova-plugin-file
## Supported Platforms
- Android
- iOS
- OS X
- Windows*
- Browser
\* _These platforms do not support `FileReader.readAsArrayBuffer` nor `FileWriter.write(blob)`._
## Where to Store Files
As of v1.2.0, URLs to important file-system directories are provided.
Each URL is in the form _file:///path/to/spot/_, and can be converted to a
`DirectoryEntry` using `window.resolveLocalFileSystemURL()`.
* `cordova.file.applicationDirectory` - Read-only directory where the application
is installed. (_iOS_, _Android_, _BlackBerry 10_, _OSX_, _windows_)
* `cordova.file.applicationStorageDirectory` - Root directory of the application's
sandbox; on iOS & windows this location is read-only (but specific subdirectories [like
`/Documents` on iOS or `/localState` on windows] are read-write). All data contained within
is private to the app. (_iOS_, _Android_, _BlackBerry 10_, _OSX_)
* `cordova.file.dataDirectory` - Persistent and private data storage within the
application's sandbox using internal memory (on Android, if you need to use
external memory, use `.externalDataDirectory`). On iOS, this directory is not
synced with iCloud (use `.syncedDataDirectory`). (_iOS_, _Android_, _BlackBerry 10_, _windows_)
* `cordova.file.cacheDirectory` - Directory for cached data files or any files
that your app can re-create easily. The OS may delete these files when the device
runs low on storage, nevertheless, apps should not rely on the OS to delete files
in here. (_iOS_, _Android_, _BlackBerry 10_, _OSX_, _windows_)
* `cordova.file.externalApplicationStorageDirectory` - Application space on
external storage. (_Android_)
* `cordova.file.externalDataDirectory` - Where to put app-specific data files on
external storage. (_Android_)
* `cordova.file.externalCacheDirectory` - Application cache on external storage.
(_Android_)
* `cordova.file.externalRootDirectory` - External storage (SD card) root. (_Android_, _BlackBerry 10_)
* `cordova.file.tempDirectory` - Temp directory that the OS can clear at will. Do not
rely on the OS to clear this directory; your app should always remove files as
applicable. (_iOS_, _OSX_, _windows_)
* `cordova.file.syncedDataDirectory` - Holds app-specific files that should be synced
(e.g. to iCloud). (_iOS_, _windows_)
* `cordova.file.documentsDirectory` - Files private to the app, but that are meaningful
to other application (e.g. Office files). Note that for _OSX_ this is the user's `~/Documents` directory. (_iOS_, _OSX_)
* `cordova.file.sharedDirectory` - Files globally available to all applications (_BlackBerry 10_)
## File System Layouts
Although technically an implementation detail, it can be very useful to know how
the `cordova.file.*` properties map to physical paths on a real device.
### iOS File System Layout
| Device Path | `cordova.file.*` | `iosExtraFileSystems` | r/w? | persistent? | OS clears | sync | private |
|:-----------------------------------------------|:----------------------------|:----------------------|:----:|:-----------:|:---------:|:----:|:-------:|
| `/var/mobile/Applications/<UUID>/` | applicationStorageDirectory | - | r | N/A | N/A | N/A | Yes |
| &nbsp;&nbsp;&nbsp;`appname.app/` | applicationDirectory | bundle | r | N/A | N/A | N/A | Yes |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`www/` | - | - | r | N/A | N/A | N/A | Yes |
| &nbsp;&nbsp;&nbsp;`Documents/` | documentsDirectory | documents | r/w | Yes | No | Yes | Yes |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`NoCloud/` | - | documents-nosync | r/w | Yes | No | No | Yes |
| &nbsp;&nbsp;&nbsp;`Library` | - | library | r/w | Yes | No | Yes? | Yes |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`NoCloud/` | dataDirectory | library-nosync | r/w | Yes | No | No | Yes |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`Cloud/` | syncedDataDirectory | - | r/w | Yes | No | Yes | Yes |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`Caches/` | cacheDirectory | cache | r/w | Yes* | Yes\*\*\*| No | Yes |
| &nbsp;&nbsp;&nbsp;`tmp/` | tempDirectory | - | r/w | No\*\* | Yes\*\*\*| No | Yes |
\* Files persist across app restarts and upgrades, but this directory can
be cleared whenever the OS desires. Your app should be able to recreate any
content that might be deleted.
\*\* Files may persist across app restarts, but do not rely on this behavior. Files
are not guaranteed to persist across updates. Your app should remove files from
this directory when it is applicable, as the OS does not guarantee when (or even
if) these files are removed.
\*\*\* The OS may clear the contents of this directory whenever it feels it is
necessary, but do not rely on this. You should clear this directory as
appropriate for your application.
### Android File System Layout
| Device Path | `cordova.file.*` | `AndroidExtraFileSystems` | r/w? | persistent? | OS clears | private |
|:------------------------------------------------|:----------------------------|:--------------------------|:----:|:-----------:|:---------:|:-------:|
| `file:///android_asset/` | applicationDirectory | assets | r | N/A | N/A | Yes |
| `/data/data/<app-id>/` | applicationStorageDirectory | - | r/w | N/A | N/A | Yes |
| &nbsp;&nbsp;&nbsp;`cache` | cacheDirectory | cache | r/w | Yes | Yes\* | Yes |
| &nbsp;&nbsp;&nbsp;`files` | dataDirectory | files | r/w | Yes | No | Yes |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`Documents` | | documents | r/w | Yes | No | Yes |
| `<sdcard>/` | externalRootDirectory | sdcard | r/w\*\*\* | Yes | No | No |
| &nbsp;&nbsp;&nbsp;`Android/data/<app-id>/` | externalApplicationStorageDirectory | - | r/w | Yes | No | No |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`cache` | externalCacheDirectory | cache-external | r/w | Yes | No\*\*| No |
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`files` | externalDataDirectory | files-external | r/w | Yes | No | No |
\* The OS may periodically clear this directory, but do not rely on this behavior. Clear
the contents of this directory as appropriate for your application. Should a user
purge the cache manually, the contents of this directory are removed.
\*\* The OS does not clear this directory automatically; you are responsible for managing
the contents yourself. Should the user purge the cache manually, the contents of the
directory are removed.
\*\*\* As of API 30, these directories are no longer writable.
**Note**: If external storage can't be mounted, the `cordova.file.external*`
properties are `null`.
### OS X File System Layout
| Device Path | `cordova.file.*` | `iosExtraFileSystems` | r/w? | OS clears | private |
|:-------------------------------------------------|:----------------------------|:----------------------|:----:|:---------:|:-------:|
| `/Applications/<appname>.app/` | - | bundle | r | N/A | Yes |
| &nbsp;&nbsp;&nbsp;&nbsp;`Content/Resources/` | applicationDirectory | - | r | N/A | Yes |
| `~/Library/Application Support/<bundle-id>/` | applicationStorageDirectory | - | r/w | No | Yes |
| &nbsp;&nbsp;&nbsp;&nbsp;`files/` | dataDirectory | - | r/w | No | Yes |
| `~/Documents/` | documentsDirectory | documents | r/w | No | No |
| `~/Library/Caches/<bundle-id>/` | cacheDirectory | cache | r/w | No | Yes |
| `/tmp/` | tempDirectory | - | r/w | Yes\* | Yes |
| `/` | rootDirectory | root | r/w | No\*\* | No |
**Note**: This is the layout for non sandboxed applications. I you enable sandboxing, the `applicationStorageDirectory` will be below ` ~/Library/Containers/<bundle-id>/Data/Library/Application Support`.
\* Files persist across app restarts and upgrades, but this directory can
be cleared whenever the OS desires. Your app should be able to recreate any
content that might be deleted. You should clear this directory as
appropriate for your application.
\*\* Allows access to the entire file system. This is only available for non sandboxed apps.
### Windows File System Layout
| Device Path | `cordova.file.*` | r/w? | persistent? | OS clears | private |
|:------------------------------------------------------|:----------------------------|:----:|:-----------:|:---------:|:-------:|
| `ms-appdata:///` | applicationDirectory | r | N/A | N/A | Yes |
| &nbsp;&nbsp;&nbsp;`local/` | dataDirectory | r/w | Yes | No | Yes |
| &nbsp;&nbsp;&nbsp;`temp/` | cacheDirectory | r/w | No | Yes\* | Yes |
| &nbsp;&nbsp;&nbsp;`temp/` | tempDirectory | r/w | No | Yes\* | Yes |
| &nbsp;&nbsp;&nbsp;`roaming/` | syncedDataDirectory | r/w | Yes | No | Yes |
\* The OS may periodically clear this directory
## Android Quirks
### Android Persistent storage location
There are multiple valid locations to store persistent files on an Android
device. See [this page](http://developer.android.com/guide/topics/data/data-storage.html)
for an extensive discussion of the various possibilities.
Previous versions of the plugin would choose the location of the temporary and
persistent files on startup, based on whether the device claimed that the SD
Card (or equivalent storage partition) was mounted. If the SD Card was mounted,
or if a large internal storage partition was available (such as on Nexus
devices,) then the persistent files would be stored in the root of that space.
This meant that all Cordova apps could see all of the files available on the
card.
If the SD card was not available, then previous versions would store data under
`/data/data/<packageId>`, which isolates apps from each other, but may still
cause data to be shared between users.
It is now possible to choose whether to store files in the internal file
storage location, or using the previous logic, with a preference in your
application's `config.xml` file. To do this, add one of these two lines to
`config.xml`:
<preference name="AndroidPersistentFileLocation" value="Internal" />
<preference name="AndroidPersistentFileLocation" value="Compatibility" />
Without this line, the File plugin will use `Internal` as the default. If
a preference tag is present, and is not one of these values, the application
will not start.
If your application has previously been shipped to users, using an older (pre-
3.0.0) version of this plugin, and has stored files in the persistent filesystem,
then you should set the preference to `Compatibility` if your config.xml does not specify a location for the persistent filesystem. Switching the location to
"Internal" would mean that existing users who upgrade their application may be
unable to access their previously-stored files, depending on their device.
If your application is new, or has never previously stored files in the
persistent filesystem, then the `Internal` setting is generally recommended.
### Slow recursive operations for /android_asset
Listing asset directories is really slow on Android. You can speed it up though, by
adding `src/android/build-extras.gradle` to the root of your android project (also
requires cordova-android@4.0.0 or greater).
### Permisson to write to external storage when it's not mounted on Marshmallow
Marshmallow requires the apps to ask for permissions when reading/writing to external locations. By
[default](http://developer.android.com/guide/topics/data/data-storage.html#filesExternal), your app has permission to write to
`cordova.file.applicationStorageDirectory` and `cordova.file.externalApplicationStorageDirectory`, and the plugin doesn't request permission
for these two directories unless external storage is not mounted. However due to a limitation, when external storage is not mounted, it would ask for
permission to write to `cordova.file.externalApplicationStorageDirectory`.
## iOS Quirks
- `cordova.file.applicationStorageDirectory` is read-only; attempting to store
files within the root directory will fail. Use one of the other `cordova.file.*`
properties defined for iOS (only `applicationDirectory` and `applicationStorageDirectory` are
read-only).
- `FileReader.readAsText(blob, encoding)`
- The `encoding` parameter is not supported, and UTF-8 encoding is always in effect.
### iOS Persistent storage location
There are two valid locations to store persistent files on an iOS device: the
Documents directory and the Library directory. Previous versions of the plugin
only ever stored persistent files in the Documents directory. This had the
side-effect of making all of an application's files visible in iTunes, which
was often unintended, especially for applications which handle lots of small
files, rather than producing complete documents for export, which is the
intended purpose of the directory.
It is now possible to choose whether to store files in the documents or library
directory, with a preference in your application's `config.xml` file. To do this,
add one of these two lines to `config.xml`:
<preference name="iosPersistentFileLocation" value="Library" />
<preference name="iosPersistentFileLocation" value="Compatibility" />
Without this line, the File plugin will use `Compatibility` as the default. If
a preference tag is present, and is not one of these values, the application
will not start.
If your application has previously been shipped to users, using an older (pre-
1.0) version of this plugin, and has stored files in the persistent filesystem,
then you should set the preference to `Compatibility`. Switching the location to
`Library` would mean that existing users who upgrade their application would be
unable to access their previously-stored files.
If your application is new, or has never previously stored files in the
persistent filesystem, then the `Library` setting is generally recommended.
## Browser Quirks
### Common quirks and remarks
- Each browser uses its own sandboxed filesystem. IE and Firefox use IndexedDB as a base.
All browsers use forward slash as directory separator in a path.
- Directory entries have to be created successively.
For example, the call `fs.root.getDirectory('dir1/dir2', {create:true}, successCallback, errorCallback)`
will fail if dir1 did not exist.
- The plugin requests user permission to use persistent storage at the application first start.
- Plugin supports `cdvfile://localhost` (local resources) only. I.e. external resources are not supported via `cdvfile`.
- The plugin does not follow ["File System API 8.3 Naming restrictions"](http://www.w3.org/TR/2011/WD-file-system-api-20110419/#naming-restrictions).
- Blob and File' `close` function is not supported.
- `FileSaver` and `BlobBuilder` are not supported by this plugin and don't have stubs.
- The plugin does not support `requestAllFileSystems`. This function is also missing in the specifications.
- Entries in directory will not be removed if you use `create: true` flag for existing directory.
- Files created via constructor are not supported. You should use entry.file method instead.
- Each browser uses its own form for blob URL references.
- `readAsDataURL` function is supported, but the mediatype in Chrome depends on entry name extension,
mediatype in IE is always empty (which is the same as `text-plain` according the specification),
the mediatype in Firefox is always `application/octet-stream`.
For example, if the content is `abcdefg` then Firefox returns `data:application/octet-stream;base64,YWJjZGVmZw==`,
IE returns `data:;base64,YWJjZGVmZw==`, Chrome returns `data:<mediatype depending on extension of entry name>;base64,YWJjZGVmZw==`.
- `toInternalURL` returns the path in the form `file:///persistent/path/to/entry` (Firefox, IE).
Chrome returns the path in the form `cdvfile://localhost/persistent/file`.
### Chrome quirks
- Chrome filesystem is not immediately ready after device ready event. As a workaround you can subscribe to `filePluginIsReady` event.
Example:
```javascript
window.addEventListener('filePluginIsReady', function(){ console.log('File plugin is ready');}, false);
```
You can use `window.isFilePluginReadyRaised` function to check whether event was already raised.
- window.requestFileSystem TEMPORARY and PERSISTENT filesystem quotas are not limited in Chrome.
- To increase persistent storage in Chrome you need to call `window.initPersistentFileSystem` method. Persistent storage quota is 5 MB by default.
- Chrome requires `--allow-file-access-from-files` run argument to support API via `file:///` protocol.
- `File` object will be not changed if you use flag `{create:true}` when getting an existing `Entry`.
- events `cancelable` property is set to true in Chrome. This is contrary to the [specification](http://dev.w3.org/2009/dap/file-system/file-writer.html).
- `toURL` function in Chrome returns `filesystem:`-prefixed path depending on application host.
For example, `filesystem:file:///persistent/somefile.txt`, `filesystem:http://localhost:8080/persistent/somefile.txt`.
- `toURL` function result does not contain trailing slash in case of directory entry.
Chrome resolves directories with slash-trailed urls correctly though.
- `resolveLocalFileSystemURL` method requires the inbound `url` to have `filesystem` prefix. For example, `url` parameter for `resolveLocalFileSystemURL`
should be in the form `filesystem:file:///persistent/somefile.txt` as opposed to the form `file:///persistent/somefile.txt` in Android.
- Deprecated `toNativeURL` function is not supported and does not have a stub.
- `setMetadata` function is not stated in the specifications and not supported.
- INVALID_MODIFICATION_ERR (code: 9) is thrown instead of SYNTAX_ERR(code: 8) on requesting of a non-existant filesystem.
- INVALID_MODIFICATION_ERR (code: 9) is thrown instead of PATH_EXISTS_ERR(code: 12) on trying to exclusively create a file or directory, which already exists.
- INVALID_MODIFICATION_ERR (code: 9) is thrown instead of NO_MODIFICATION_ALLOWED_ERR(code: 6) on trying to call removeRecursively on the root file system.
- INVALID_MODIFICATION_ERR (code: 9) is thrown instead of NOT_FOUND_ERR(code: 1) on trying to moveTo directory that does not exist.
### IndexedDB-based impl quirks (Firefox and IE)
- `.` and `..` are not supported.
- IE does not support `file:///`-mode; only hosted mode is supported (http://localhost:xxxx).
- Firefox filesystem size is not limited but each 50MB extension will request a user permission.
IE10 allows up to 10mb of combined AppCache and IndexedDB used in implementation of filesystem without prompting,
once you hit that level you will be asked if you want to allow it to be increased up to a max of 250mb per site.
So `size` parameter for `requestFileSystem` function does not affect filesystem in Firefox and IE.
- `readAsBinaryString` function is not stated in the Specs and not supported in IE and does not have a stub.
- `file.type` is always null.
- You should not create entry using DirectoryEntry instance callback result which was deleted.
Otherwise, you will get a 'hanging entry'.
- Before you can read a file, which was just written you need to get a new instance of this file.
- `setMetadata` function, which is not stated in the Specs supports `modificationTime` field change only.
- `copyTo` and `moveTo` functions do not support directories.
- Directories metadata is not supported.
- Both Entry.remove and directoryEntry.removeRecursively don't fail when removing
non-empty directories - directories being removed are cleaned along with contents instead.
- `abort` and `truncate` functions are not supported.
- progress events are not fired. For example, this handler will be not executed:
```javascript
writer.onprogress = function() { /*commands*/ };
```
## Upgrading Notes
In v1.0.0 of this plugin, the `FileEntry` and `DirectoryEntry` structures have changed,
to be more in line with the published specification.
Previous (pre-1.0.0) versions of the plugin stored the device-absolute-file-location
in the `fullPath` property of `Entry` objects. These paths would typically look like
/var/mobile/Applications/<application UUID>/Documents/path/to/file (iOS)
/storage/emulated/0/path/to/file (Android)
These paths were also returned by the `toURL()` method of the `Entry` objects.
With v1.0.0, the `fullPath` attribute is the path to the file, _relative to the root of
the HTML filesystem_. So, the above paths would now both be represented by a `FileEntry`
object with a `fullPath` of
/path/to/file
If your application works with device-absolute-paths, and you previously retrieved those
paths through the `fullPath` property of `Entry` objects, then you should update your code
to use `entry.toURL()` instead.
For backwards compatibility, the `resolveLocalFileSystemURL()` method will accept a
device-absolute-path, and will return an `Entry` object corresponding to it, as long as that
file exists within either the `TEMPORARY` or `PERSISTENT` filesystems.
This has particularly been an issue with the File-Transfer plugin, which previously used
device-absolute-paths (and can still accept them). It has been updated to work correctly
with FileSystem URLs, so replacing `entry.fullPath` with `entry.toURL()` should resolve any
issues getting that plugin to work with files on the device.
In v1.1.0 the return value of `toURL()` was changed (see [CB-6394](https://issues.apache.org/jira/browse/CB-6394))
to return an absolute 'file://' URL. wherever possible. To ensure a 'cdvfile:'-URL you can use `toInternalURL()` now.
This method will now return filesystem URLs of the form
cdvfile://localhost/persistent/path/to/file
which can be used to identify the file uniquely.
In v7.0.0 the return value of `toURL()` for Android was updated to return the absolute `file://` URL when app content is served from the `file://` scheme.
If app content is served from the `http(s)://` scheme, a `cdvfile` formatted URL will be returned instead. The `cdvfile` formatted URL is created from the internal method `toInternalURL()`.
An example `toInternalURL()` return filesystem URL:
https://localhost/persistent/path/to/file
[![toURL flow](https://sketchviz.com/@erisu/7b05499842275be93a0581e8e3576798/6dc71d8302cafd05b443d874a592d10fa415b8e3.sketchy.png)](//sketchviz.com/@erisu/7b05499842275be93a0581e8e3576798)
It is recommended to always use the `toURL()` to ensure that the correct URL is returned.
## cdvfile protocol
- Not Supported on Android
**Purpose**
`cdvfile://localhost/persistent|temporary|another-fs-root*/path/to/file` can be used for platform-independent file paths.
cdvfile paths are supported by core plugins - for example you can download an mp3 file to cdvfile-path via `cordova-plugin-file-transfer` and play it via `cordova-plugin-media`.
__*Note__: See [Where to Store Files](#where-to-store-files), [File System Layouts](#file-system-layouts) and [Configuring the Plugin](#configuring-the-plugin-optional) for more details about available fs roots.
To use `cdvfile` as a tag' `src` you can convert it to native path via `toURL()` method of the resolved fileEntry, which you can get via `resolveLocalFileSystemURL` - see examples below.
You can also use `cdvfile://` paths directly in the DOM, for example:
```HTML
<img src="cdvfile://localhost/persistent/img/logo.png" />
```
__Note__: This method requires following Content Security rules updates:
* Add `cdvfile:` scheme to `Content-Security-Policy` meta tag of the index page, e.g.:
- `<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: `**cdvfile:**` https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">`
* Add `<access origin="cdvfile://*" />` to `config.xml`.
**Converting cdvfile:// to native path**
```javascript
resolveLocalFileSystemURL('cdvfile://localhost/temporary/path/to/file.mp4', function(entry) {
var nativePath = entry.toURL();
console.log('Native URI: ' + nativePath);
document.getElementById('video').src = nativePath;
```
**Converting native path to cdvfile://**
```javascript
resolveLocalFileSystemURL(nativePath, function(entry) {
console.log('cdvfile URI: ' + entry.toInternalURL());
```
**Using cdvfile in core plugins**
```javascript
fileTransfer.download(uri, 'cdvfile://localhost/temporary/path/to/file.mp3', function (entry) { ...
```
```javascript
var my_media = new Media('cdvfile://localhost/temporary/path/to/file.mp3', ...);
my_media.play();
```
#### cdvfile quirks
- Using `cdvfile://` paths in the DOM is not supported on Windows platform (a path can be converted to native instead).
## List of Error Codes and Meanings
When an error is thrown, one of the following codes will be used.
| Code | Constant |
|-----:|:------------------------------|
| 1 | `NOT_FOUND_ERR` |
| 2 | `SECURITY_ERR` |
| 3 | `ABORT_ERR` |
| 4 | `NOT_READABLE_ERR` |
| 5 | `ENCODING_ERR` |
| 6 | `NO_MODIFICATION_ALLOWED_ERR` |
| 7 | `INVALID_STATE_ERR` |
| 8 | `SYNTAX_ERR` |
| 9 | `INVALID_MODIFICATION_ERR` |
| 10 | `QUOTA_EXCEEDED_ERR` |
| 11 | `TYPE_MISMATCH_ERR` |
| 12 | `PATH_EXISTS_ERR` |
## Configuring the Plugin (Optional)
The set of available filesystems can be configured per-platform. Both iOS and
Android recognize a <preference> tag in `config.xml` which names the
filesystems to be installed. By default, all file-system roots are enabled.
<preference name="iosExtraFilesystems" value="library,library-nosync,documents,documents-nosync,cache,bundle,root" />
<preference name="AndroidExtraFilesystems" value="files,files-external,documents,sdcard,cache,cache-external,assets,root" />
### Android
* `files`: The application's internal file storage directory
* `files-external`: The application's external file storage directory
* `sdcard`: The global external file storage directory (this is the root of the SD card, if one is installed). You must have the `android.permission.WRITE_EXTERNAL_STORAGE` permission to use this.
* `cache`: The application's internal cache directory
* `cache-external`: The application's external cache directory
* `assets`: The application's bundle (read-only)
* `root`: The entire device filesystem
* `applicationDirectory`: ReadOnly with restricted access. Copying files in this directory is possible, but reading it directly results in 'file not found'.
Android also supports a special filesystem named "documents", which represents a "/Documents/" subdirectory within the "files" filesystem.
### iOS
* `library`: The application's Library directory
* `documents`: The application's Documents directory
* `cache`: The application's Cache directory
* `bundle`: The application's bundle; the location of the app itself on disk (read-only)
* `root`: The entire device filesystem
By default, the library and documents directories can be synced to iCloud. You can also request two additional filesystems, `library-nosync` and `documents-nosync`, which represent a special non-synced directory within the `/Library` or `/Documents` filesystem.
## Sample: Create Files and Directories, Write, Read, and Append files <a name="sample"></a>
The File plugin allows you to do things like store files in a temporary or persistent storage location for your app (sandboxed storage) and to store files in other platform-dependent locations. The code snippets in this section demonstrate different tasks including:
* [Accessing the file system](#persistent)
* Using cross-platform Cordova file URLs to [store your files](#appendFile) (see _Where to Store Files_ for more info)
* Creating [files](#persistent) and [directories](#createDir)
* [Writing to files](#writeFile)
* [Reading files](#readFile)
* [Appending files](#appendFile)
* [Display an image file](#displayImage)
## Create a persistent file <a name="persistent"></a>
Before you use the File plugin APIs, you can get access to the file system using `requestFileSystem`. When you do this, you can request either persistent or temporary storage. Persistent storage will not be removed unless permission is granted by the user.
When you get file system access using `requestFileSystem`, access is granted for the sandboxed file system only (the sandbox limits access to the app itself), not for general access to any file system location on the device. (To access file system locations outside the sandboxed storage, use other methods such as window.resolveLocalFileSystemURL, which support platform-specific locations. For one example of this, see _Append a File_.)
Here is a request for persistent storage.
>*Note* When targeting WebView clients (instead of a browser) or native apps (Windows), you dont need to use `requestQuota` before using persistent storage.
```js
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fs) {
console.log('file system open: ' + fs.name);
fs.root.getFile("newPersistentFile.txt", { create: true, exclusive: false }, function (fileEntry) {
console.log("fileEntry is file?" + fileEntry.isFile.toString());
// fileEntry.name == 'someFile.txt'
// fileEntry.fullPath == '/someFile.txt'
writeFile(fileEntry, null);
}, onErrorCreateFile);
}, onErrorLoadFs);
```
The success callback receives FileSystem object (fs). Use `fs.root` to return a DirectoryEntry object, which you can use to create or get a file (by calling `getFile`). In this example, `fs.root` is a DirectoryEntry object that represents the persistent storage in the sandboxed file system.
The success callback for `getFile` receives a FileEntry object. You can use this to perform file write and file read operations.
## Create a temporary file
Here is an example of a request for temporary storage. Temporary storage may be deleted by the operating system if the device runs low on memory.
```js
window.requestFileSystem(window.TEMPORARY, 5 * 1024 * 1024, function (fs) {
console.log('file system open: ' + fs.name);
createFile(fs.root, "newTempFile.txt", false);
}, onErrorLoadFs);
```
When you are using temporary storage, you can create or get the file by calling `getFile`. As in the persistent storage example, this will give you a FileEntry object that you can use for read or write operations.
```js
function createFile(dirEntry, fileName, isAppend) {
// Creates a new file or returns the file if it already exists.
dirEntry.getFile(fileName, {create: true, exclusive: false}, function(fileEntry) {
writeFile(fileEntry, null, isAppend);
}, onErrorCreateFile);
}
```
## Write to a file <a name="writeFile"></a>
Once you have a FileEntry object, you can write to the file by calling `createWriter`, which returns a FileWriter object in the success callback. Call the `write` method of FileWriter to write to the file.
```js
function writeFile(fileEntry, dataObj) {
// Create a FileWriter object for our FileEntry (log.txt).
fileEntry.createWriter(function (fileWriter) {
fileWriter.onwriteend = function() {
console.log("Successful file write...");
readFile(fileEntry);
};
fileWriter.onerror = function (e) {
console.log("Failed file write: " + e.toString());
};
// If data object is not passed in,
// create a new Blob instead.
if (!dataObj) {
dataObj = new Blob(['some file data'], { type: 'text/plain' });
}
fileWriter.write(dataObj);
});
}
```
## Read a file <a name="readFile"></a>
You also need a FileEntry object to read an existing file. Use the file property of FileEntry to get the file reference, and then create a new FileReader object. You can use methods like `readAsText` to start the read operation. When the read operation is complete, `this.result` stores the result of the read operation.
```js
function readFile(fileEntry) {
fileEntry.file(function (file) {
var reader = new FileReader();
reader.onloadend = function() {
console.log("Successful file read: " + this.result);
displayFileData(fileEntry.fullPath + ": " + this.result);
};
reader.readAsText(file);
}, onErrorReadFile);
}
```
## Append a file using alternative methods <a name="appendFile"></a>
Of course, you will often want to append existing files instead of creating new ones. Here is an example of that. This example shows another way that you can access the file system using window.resolveLocalFileSystemURL. In this example, pass the cross-platform Cordova file URL, cordova.file.dataDirectory, to the function. The success callback receives a DirectoryEntry object, which you can use to do things like create a file.
```js
window.resolveLocalFileSystemURL(cordova.file.dataDirectory, function (dirEntry) {
console.log('file system open: ' + dirEntry.name);
var isAppend = true;
createFile(dirEntry, "fileToAppend.txt", isAppend);
}, onErrorLoadFs);
```
In addition to this usage, you can use `resolveLocalFileSystemURL` to get access to some file system locations that are not part of the sandboxed storage system. See _Where to store Files_ for more information; many of these storage locations are platform-specific. You can also pass cross-platform file system locations to `resolveLocalFileSystemURL` using the _cdvfile protocol_.
For the append operation, there is nothing new in the `createFile` function that is called in the preceding code (see the preceding examples for the actual code). `createFile` calls `writeFile`. In `writeFile`, you check whether an append operation is requested.
Once you have a FileWriter object, call the `seek` method, and pass in the index value for the position where you want to write. In this example, you also test whether the file exists. After calling seek, then call the write method of FileWriter.
```js
function writeFile(fileEntry, dataObj, isAppend) {
// Create a FileWriter object for our FileEntry (log.txt).
fileEntry.createWriter(function (fileWriter) {
fileWriter.onwriteend = function() {
console.log("Successful file read...");
readFile(fileEntry);
};
fileWriter.onerror = function (e) {
console.log("Failed file read: " + e.toString());
};
// If we are appending data to file, go to the end of the file.
if (isAppend) {
try {
fileWriter.seek(fileWriter.length);
}
catch (e) {
console.log("file doesn't exist!");
}
}
fileWriter.write(dataObj);
});
}
```
## Store an existing binary file <a name="binaryFile"></a>
We already showed how to write to a file that you just created in the sandboxed file system. What if you need to get access to an existing file and convert that to something you can store on your device? In this example, you obtain a file using an xhr request, and then save it to the cache in the sandboxed file system.
Before you get the file, get a FileSystem reference using `requestFileSystem`. By passing window.TEMPORARY in the method call (same as before), the returned FileSystem object (fs) represents the cache in the sandboxed file system. Use `fs.root` to get the DirectoryEntry object that you need.
```js
window.requestFileSystem(window.TEMPORARY, 5 * 1024 * 1024, function (fs) {
console.log('file system open: ' + fs.name);
getSampleFile(fs.root);
}, onErrorLoadFs);
```
For completeness, here is the xhr request to get a Blob image. There is nothing Cordova-specific in this code, except that you forward the DirectoryEntry reference that you already obtained as an argument to the saveFile function. You will save the blob image and display it later after reading the file (to validate the operation).
```js
function getSampleFile(dirEntry) {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://cordova.apache.org/static/img/cordova_bot.png', true);
xhr.responseType = 'blob';
xhr.onload = function() {
if (this.status == 200) {
var blob = new Blob([this.response], { type: 'image/png' });
saveFile(dirEntry, blob, "downloadedImage.png");
}
};
xhr.send();
}
```
>*Note* For Cordova 5 security, the preceding code requires that you add the domain name, http://cordova.apache.org, to the Content-Security-Policy <meta> element in index.html.
After getting the file, copy the contents to a new file. The current DirectoryEntry object is already associated with the app cache.
```js
function saveFile(dirEntry, fileData, fileName) {
dirEntry.getFile(fileName, { create: true, exclusive: false }, function (fileEntry) {
writeFile(fileEntry, fileData);
}, onErrorCreateFile);
}
```
In writeFile, you pass in the Blob object as the dataObj and you will save that in the new file.
```js
function writeFile(fileEntry, dataObj, isAppend) {
// Create a FileWriter object for our FileEntry (log.txt).
fileEntry.createWriter(function (fileWriter) {
fileWriter.onwriteend = function() {
console.log("Successful file write...");
if (dataObj.type == "image/png") {
readBinaryFile(fileEntry);
}
else {
readFile(fileEntry);
}
};
fileWriter.onerror = function(e) {
console.log("Failed file write: " + e.toString());
};
fileWriter.write(dataObj);
});
}
```
After writing to the file, read it and display it. You saved the image as binary data, so you can read it using FileReader.readAsArrayBuffer.
```js
function readBinaryFile(fileEntry) {
fileEntry.file(function (file) {
var reader = new FileReader();
reader.onloadend = function() {
console.log("Successful file write: " + this.result);
displayFileData(fileEntry.fullPath + ": " + this.result);
var blob = new Blob([new Uint8Array(this.result)], { type: "image/png" });
displayImage(blob);
};
reader.readAsArrayBuffer(file);
}, onErrorReadFile);
}
```
After reading the data, you can display the image using code like this. Use window.URL.createObjectURL to get a DOM string for the Blob image.
```js
function displayImage(blob) {
// Displays image if result is a valid DOM string for an image.
var elem = document.getElementById('imageFile');
// Note: Use window.URL.revokeObjectURL when finished with image.
elem.src = window.URL.createObjectURL(blob);
}
```
## Display an image file <a name="displayImage"></a>
To display an image using a FileEntry, you can call the `toURL` method.
```js
function displayImageByFileURL(fileEntry) {
var elem = document.getElementById('imageFile');
elem.src = fileEntry.toURL();
}
```
If you are using some platform-specific URIs instead of a FileEntry and you want to display an image, you may need to include the main part of the URI in the Content-Security-Policy <meta> element in index.html. For example, on Windows 10, you can include `ms-appdata:` in your <meta> element. Here is an example.
```html
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: ms-appdata: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
```
## Create Directories <a name="createDir"></a>
In the code here, you create directories in the root of the app storage location. You could use this code with any writable storage location (that is, any DirectoryEntry). Here, you write to the application cache (assuming that you used window.TEMPORARY to get your FileSystem object) by passing fs.root into this function.
This code creates the /NewDirInRoot/images folder in the application cache. For platform-specific values, look at _File System Layouts_.
```js
function createDirectory(rootDirEntry) {
rootDirEntry.getDirectory('NewDirInRoot', { create: true }, function (dirEntry) {
dirEntry.getDirectory('images', { create: true }, function (subDirEntry) {
createFile(subDirEntry, "fileInNewSubDir.txt");
}, onErrorGetDir);
}, onErrorGetDir);
}
```
When creating subfolders, you need to create each folder separately as shown in the preceding code.

View File

@@ -0,0 +1,528 @@
<!--
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
-->
# Release Notes
### 7.0.0 (Apr 08, 2022)
* [GH-520](https://github.com/apache/cordova-plugin-file/pull/520) test(android): disable `content` scheme tests
* [GH-519](https://github.com/apache/cordova-plugin-file/pull/519) chore!: removed old platform code & lint cleanup
* [GH-518](https://github.com/apache/cordova-plugin-file/pull/518) test(android): fix tests to use `cordova-android` 10.x default https scheme
* [GH-517](https://github.com/apache/cordova-plugin-file/pull/517) fix(android): support cdvfile assets for custom scheme
* [GH-516](https://github.com/apache/cordova-plugin-file/pull/516) fix(android): create `toURL` override to preserve other platforms
* [GH-515](https://github.com/apache/cordova-plugin-file/pull/515) chore(npm): bump `package-lock.json` to v2
* [GH-487](https://github.com/apache/cordova-plugin-file/pull/487) fix(android): Request external read permission when listing external directories
* [GH-513](https://github.com/apache/cordova-plugin-file/pull/513) feat(android): add `WebViewAssetLoader` proxy handler for cdvfile
* [GH-501](https://github.com/apache/cordova-plugin-file/pull/501) ci(ios): update workflow w/ **iOS** 15
* [GH-498](https://github.com/apache/cordova-plugin-file/pull/498) ci: add action-badge
* [GH-497](https://github.com/apache/cordova-plugin-file/pull/497) ci: remove `travis` & `appveyor`
* [GH-490](https://github.com/apache/cordova-plugin-file/pull/490) chore: `npmrc`
* [GH-493](https://github.com/apache/cordova-plugin-file/pull/493) test: remove deprecated `cordova-plugin-contact` tests
* [GH-492](https://github.com/apache/cordova-plugin-file/pull/492) ci: remove `ci.yml` infavor of `lint.yml`
* [GH-491](https://github.com/apache/cordova-plugin-file/pull/491) ci: add gh-actions workflows
* [GH-441](https://github.com/apache/cordova-plugin-file/pull/441) ci: add node-14.x to workflow
* [GH-489](https://github.com/apache/cordova-plugin-file/pull/489) fix: Brought back the return statement
* [GH-470](https://github.com/apache/cordova-plugin-file/pull/470) fix: Remove test log
* [GH-467](https://github.com/apache/cordova-plugin-file/pull/467) docs: adds missing words to the file object section in the `README`
* [GH-458](https://github.com/apache/cordova-plugin-file/pull/458) refactor: shared `eslint` config
* [GH-447](https://github.com/apache/cordova-plugin-file/pull/447) fix(browser): typo in `preparing.js`
* [GH-439](https://github.com/apache/cordova-plugin-file/pull/439) chore: Require **Android** 9
* [GH-433](https://github.com/apache/cordova-plugin-file/pull/433) refactor(android): Removed obsolete copyResource function
* [GH-411](https://github.com/apache/cordova-plugin-file/pull/411) test: (android) use API 29
* [GH-417](https://github.com/apache/cordova-plugin-file/pull/417) fix(android): Use legacy storage
* [GH-404](https://github.com/apache/cordova-plugin-file/pull/404) chore(npm): use short notation in `package.json`
* chore(asf): update git notification settings
* Update CONTRIBUTING.md
* [GH-383](https://github.com/apache/cordova-plugin-file/pull/383) chore(npm): improve ignore list
### 6.0.2 (Jun 27, 2019)
- fix: manually fix line endings of some files that were using CRLF ([`e8e06c3`](https://github.com/apache/cordova-plugin-file/commit/e8e06c3))
- chore: fix repo and issue urls and license in package.json and plugin.xml ([`1bca166`](https://github.com/apache/cordova-plugin-file/commit/1bca166))
- docs: remove outdated translations ([`a898b85`](https://github.com/apache/cordova-plugin-file/commit/a898b85))
- build: add `.gitattributes` to force LF (instead of possible CRLF on Windows) ([`d691a04`](https://github.com/apache/cordova-plugin-file/commit/d691a04))
- build: add `.npmignore` to remove unneeded files from npm package ([`a255202`](https://github.com/apache/cordova-plugin-file/commit/a255202))
- ci(travis): Update Travis CI configuration for new paramedic ([#314](https://github.com/apache/cordova-plugin-file/issues/314)) ([`2b93a67`](https://github.com/apache/cordova-plugin-file/commit/2b93a67))
- fix(browser): support safari private browsing mode by using base64 text instead of blob ([#301](https://github.com/apache/cordova-plugin-file/issues/301)) ([`c609ff6`](https://github.com/apache/cordova-plugin-file/commit/c609ff6))
- chore(github): Add or update GitHub pull request and issue template ([`b762743`](https://github.com/apache/cordova-plugin-file/commit/b762743))
- fix(types): [CB-13569](https://issues.apache.org/jira/browse/CB-13569) fix inverted LocalFileSystem enum ([#274](https://github.com/apache/cordova-plugin-file/issues/274)) ([`d135cd0`](https://github.com/apache/cordova-plugin-file/commit/d135cd0))
- docs: remove JIRA link ([`341fa9c`](https://github.com/apache/cordova-plugin-file/commit/341fa9c))
- fix(types): [CB-13850](https://issues.apache.org/jira/browse/CB-13850) Fix spelling in typed-file: property name filesystem (wrong… ([#229](https://github.com/apache/cordova-plugin-file/issues/229)) ([`4642fde`](https://github.com/apache/cordova-plugin-file/commit/4642fde))
- fix(types): [CB-13960](https://issues.apache.org/jira/browse/CB-13960) fix FileWriter.write argument type definition for typescript ([#231](https://github.com/apache/cordova-plugin-file/issues/231)) ([`5353b84`](https://github.com/apache/cordova-plugin-file/commit/5353b84))
- ci(travis): also accept terms for android sdk `android-27` ([`6e7871b`](https://github.com/apache/cordova-plugin-file/commit/6e7871b))
- docs: add details for `applicationDirectory` on android ([#234](https://github.com/apache/cordova-plugin-file/issues/234)) ([`c2f5216`](https://github.com/apache/cordova-plugin-file/commit/c2f5216))
- fix: require FileReader in FileWriter to fix 'write' exec not being called ([#237](https://github.com/apache/cordova-plugin-file/issues/237)) ([`4a92bbb`](https://github.com/apache/cordova-plugin-file/commit/4a92bbb))
- fix(android): [CB-14181](https://issues.apache.org/jira/browse/CB-14181) (android) Fix bug - Cannot read property 'filesystemName' of null ([#235](https://github.com/apache/cordova-plugin-file/issues/235)) ([`cc3aedb`](https://github.com/apache/cordova-plugin-file/commit/cc3aedb))
- ci(travis): [CB-13753](https://issues.apache.org/jira/browse/CB-13753) Add build-tools-26.0.2 to travis ([#228](https://github.com/apache/cordova-plugin-file/issues/228)) ([`d8cc0fd`](https://github.com/apache/cordova-plugin-file/commit/d8cc0fd), [`5e12b5e`](https://github.com/apache/cordova-plugin-file/commit/5e12b5e))
- chore: Fix release notes ([#227](https://github.com/apache/cordova-plugin-file/issues/227)) ([`c248827`](https://github.com/apache/cordova-plugin-file/commit/c248827))
### 6.0.1 (Dec 27, 2017)
* [CB-13704](https://issues.apache.org/jira/browse/CB-13704) Fix to allow 6.0.0 version install
### 6.0.0 (Dec 15, 2017)
* [CB-13668](https://issues.apache.org/jira/browse/CB-13668) Remove deprecated platforms
### 5.0.0 (Nov 06, 2017)
* [CB-13481](https://issues.apache.org/jira/browse/CB-13481) (android) Don't ask for permission to read `file:///android_asset/`
* [CB-13518](https://issues.apache.org/jira/browse/CB-13518) Add 'protective' entry to `cordovaDependencies`
* [CB-13472](https://issues.apache.org/jira/browse/CB-13472) (CI) Fixed Travis **Android** builds again
* [CB-13294](https://issues.apache.org/jira/browse/CB-13294) Remove `cordova-plugin-compat`
* fixing `README` in use of `window.resolveLocalFileSystemURL`
* [CB-12895](https://issues.apache.org/jira/browse/CB-12895) setup `eslint` and took out `jshint`
* [CB-13028](https://issues.apache.org/jira/browse/CB-13028) (CI) **Browser** builds on Travis
* [CB-13000](https://issues.apache.org/jira/browse/CB-13000) (CI) Speed up **Android** builds
* [CB-12355](https://issues.apache.org/jira/browse/CB-12355) (iOS) add desciption about the `mimeTypeForFileAtPath` method
* [CB-12355](https://issues.apache.org/jira/browse/CB-12355) (iOS) fix `FileEntry.file.type`
* [CB-12847](https://issues.apache.org/jira/browse/CB-12847) added `bugs` entry to `package.json`.
### 4.3.3 (Apr 27, 2017)
* [CB-12622](https://issues.apache.org/jira/browse/CB-12622) Added **Android 6.0** build badge to `README`
* [CB-12685](https://issues.apache.org/jira/browse/CB-12685) added `package.json` to tests folder
### 4.3.2 (Feb 28, 2017)
* [CB-12353](https://issues.apache.org/jira/browse/CB-12353) Corrected merges usage in `plugin.xml`
* [CB-12369](https://issues.apache.org/jira/browse/CB-12369) Add plugin typings from `DefinitelyTyped`
* [CB-12363](https://issues.apache.org/jira/browse/CB-12363) Added build badges for **iOS 9.3** and **iOS 10.0**
* [CB-12230](https://issues.apache.org/jira/browse/CB-12230) Removed **Windows 8.1** build badges
### 4.3.1 (Dec 07, 2016)
* [CB-12224](https://issues.apache.org/jira/browse/CB-12224) Updated version and RELEASENOTES.md for release 4.3.1
* [CB-12112](https://issues.apache.org/jira/browse/CB-12112) windows: Make available to move folder trees
* fix ENCODING_ERR for applicationDirectory
* [CB-11848](https://issues.apache.org/jira/browse/CB-11848) windows: Remove duplicate slash after file system path
* [CB-11917](https://issues.apache.org/jira/browse/CB-11917) - Remove pull request template checklist item: "iCLA has been submitted…"
* [CB-11947](https://issues.apache.org/jira/browse/CB-11947) fixed typo that occurs when adding file-transfer plugin
* [CB-11832](https://issues.apache.org/jira/browse/CB-11832) Incremented plugin version.
### 4.3.0 (Sep 08, 2016)
* [CB-11795](https://issues.apache.org/jira/browse/CB-11795) Add 'protective' entry to cordovaDependencies
* Add handling for `SecurityException`
* [CB-11368](https://issues.apache.org/jira/browse/CB-11368) **android**: Resolve content `URLs` produced by contacts plugin
* Plugin uses `Android Log class` and not `Cordova LOG class`
* [CB-11693](https://issues.apache.org/jira/browse/CB-11693) **ios**: Run copy and move operations in the background thread
* [CB-11699](https://issues.apache.org/jira/browse/CB-11699) Read files as Data URLs properly
* [CB-11305](https://issues.apache.org/jira/browse/CB-11305) Enable `cdvfile: assets fs root` for `DOM` requests
* [CB-11385](https://issues.apache.org/jira/browse/CB-11385) android: Import java.nio.charset.Charset in LocalFileSystem class
* Add badges for paramedic builds on Jenkins
* [CB-11407](https://issues.apache.org/jira/browse/CB-11407) ios: added extern keyword to constants to fix phonegap-webview-ios template issue.
* [CB-11385](https://issues.apache.org/jira/browse/CB-11385) **android**: Does not pass sonarqube scan
* Add pull request template.
* Minor edits to the `README.md`
* [CB-11142](https://issues.apache.org/jira/browse/CB-11142) Fix the `NeedPermission` code for the case when external media is not mounted in Android
* [CB-11003](https://issues.apache.org/jira/browse/CB-11003) Adding samples to Readme.
* [CB-10996](https://issues.apache.org/jira/browse/CB-10996) Adding front matter to README.md
* [CB-11115](https://issues.apache.org/jira/browse/CB-11115) **android**: Removing dependency on FileDescriptor toString in content provider tests
### 4.2.0 (Apr 15, 2016)
* [CB-10960](https://issues.apache.org/jira/browse/CB-10960) Uncaught `#<FileError>` in `write()` when `readyState != WRITING ?`
* Replace `PermissionHelper.java` with `cordova-plugin-compat`
* [CB-10977](https://issues.apache.org/jira/browse/CB-10977) **Android** Removing global state used for permission requests
* CB-10798, [CB-10384](https://issues.apache.org/jira/browse/CB-10384) Fixing permissions for **Marshmallow**.
* Fix test failure on **WP 8.1**
* [CB-10577](https://issues.apache.org/jira/browse/CB-10577) **Windows** `resolveLocalFileSystemURL` should omit trailing slash for file
* [CB-7862](https://issues.apache.org/jira/browse/CB-7862) `FileReader` reads large files in chunks with progress.
* [CB-10577](https://issues.apache.org/jira/browse/CB-10577) **Android** `resolveLocalFileSystemURL` should detect directory vs file.
* [CB-9753](https://issues.apache.org/jira/browse/CB-9753) index out of bounds on `requestFileSystem`.
* Remove `warning` emoji, as it doesn't correctly display in the docs website: cordova.apache.org/docs/en/dev/cordova-plugin-file/index.html. This closes #166
* [CB-10636](https://issues.apache.org/jira/browse/CB-10636) Add `JSHint` for plugins
* [CB-10411](https://issues.apache.org/jira/browse/CB-10411) Error in `file.spec.129` of `cordova-plugin-file`
### 4.1.1 (Feb 09, 2016)
* Edit package.json license to match SPDX id
* [CB-10419](https://issues.apache.org/jira/browse/CB-10419) cordova-plugin-file 4.0.0 error with browserify workflow
### 4.1.0 (Jan 15, 2016)
* added `.ratignore` file
* [CB-10319](https://issues.apache.org/jira/browse/CB-10319) **android** Adding reflective helper methods for permission requests
* [CB-10023](https://issues.apache.org/jira/browse/CB-10023) Fix `proxy not found error` on Chrome.
* [CB-8863](https://issues.apache.org/jira/browse/CB-8863) **ios** Fix block usage of self
### 4.0.0 (Nov 18, 2015)
* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated `RELEASENOTES` to be newest to oldest
* [CB-8497](https://issues.apache.org/jira/browse/CB-8497) Fix handling of file paths with `#` character
* Do not inject default `AndroidPersistentFileLocation` into `config.xml`
* [CB-9891](https://issues.apache.org/jira/browse/CB-9891): Fix permission errors due to `URI encoding` inconsistency on **Android**
* Fixed `NullPointer Exception` in **Android 5** and above due to invalid column name on cursor
* Fix default persistent file location
* fix `applicationDirectory` to use `ms-appx:///`
* Add **Windows** paths to `cordova.file` object
* [CB-9851](https://issues.apache.org/jira/browse/CB-9851) Document `cdvfile` protocol quirk - using `cdvfile://` in the `DOM` is not supported on **Windows**
* [CB-9752](https://issues.apache.org/jira/browse/CB-9752) `getDirectory` fails on valid directory with assets filesystem
* [CB-7253](https://issues.apache.org/jira/browse/CB-7253) `requestFileSystem` fails when no external storage is present
* Adding permissions for **Marshmallow**. Now supports **Anrdoid 6.0**
* Fixing contribute link.
* always use setters to fix memory issues without `ARC` for **iOS**
* [CB-9331](https://issues.apache.org/jira/browse/CB-9331) `getFreeDiskSpace` **iOS**.
* override `resolveLocalFileSystemURL` by `webkitResolveLocalFileSystemURL` for **browser** platform add `.project` into git ignore list
* Fail with `FileError.ENCODING_ERR` on encoding exception.
* [CB-9544](https://issues.apache.org/jira/browse/CB-9544) Add file plugin for **OSX**
* [CB-9539](https://issues.apache.org/jira/browse/CB-9539) Fixed test failure on **Android** emulator
* Added docs on `CSP` rules needed for using `cdvfile` in DOM src. This closes #120
* Added `cdvfile` protocol purpose description and examples
### 3.0.0 (Aug 18, 2015)
* Make Android default persistent file location internal
* Fixed issue with file paths not existing when using browserify
* [CB-9251](https://issues.apache.org/jira/browse/CB-9251): Changed from Intents to Preferences object as per the issue
* [CB-9215](https://issues.apache.org/jira/browse/CB-9215) Add cordova-plugin-file manual test for windows platform
### 2.1.0 (Jun 17, 2015)
* added missing license header
* [CB-9128](https://issues.apache.org/jira/browse/CB-9128) cordova-plugin-file documentation translation: cordova-plugin-file
* fix npm md
* [CB-8844](https://issues.apache.org/jira/browse/CB-8844) Increased timeout for asset tests
* Updated resolveFileSystem.js so it can be parsed by uglifyJS
* [CB-8860](https://issues.apache.org/jira/browse/CB-8860) cordova-plugin-file documentation translation: cordova-plugin-file
* [CB-8792](https://issues.apache.org/jira/browse/CB-8792) Fixes reading of json files using readAsText
### 2.0.0 (Apr 15, 2015)
* [CB-8849](https://issues.apache.org/jira/browse/CB-8849) Fixed ReadAsArrayBuffer to return ArrayBuffer and not Array on WP8
* [CB-8819](https://issues.apache.org/jira/browse/CB-8819) Fixed FileReader's readAsBinaryString on wp8
* [CB-8746](https://issues.apache.org/jira/browse/CB-8746) gave plugin major version bump
* [CB-8683](https://issues.apache.org/jira/browse/CB-8683) android: Fix broken unit tests from plugin rename
* [CB-8683](https://issues.apache.org/jira/browse/CB-8683) changed plugin-id to pacakge-name
* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) properly updated translated docs to use new id
* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) updated translated docs to use new id
* Use TRAVIS_BUILD_DIR, install paramedic by npm
* docs: added Windows to supported platforms
* [CB-8699](https://issues.apache.org/jira/browse/CB-8699) [CB-6428](https://issues.apache.org/jira/browse/CB-6428) Fix uncompressed assets being copied as zero length files
* [CB-6428](https://issues.apache.org/jira/browse/CB-6428) android: Fix assets FileEntry having size of -1
* android: Move URLforFullPath into base class (and rename to localUrlforFullPath)
* [CB-6428](https://issues.apache.org/jira/browse/CB-6428) Mention build-extras.gradle in README
* [CB-7109](https://issues.apache.org/jira/browse/CB-7109) android: Parse arguments off of the main thread (close #97)
* [CB-8695](https://issues.apache.org/jira/browse/CB-8695) ios: Fix `blob.slice()` for `asset-library` URLs (close #105)
* Tweak build-extras.gradle to just read/write to main `assets/` instead of `build/`
* [CB-8689](https://issues.apache.org/jira/browse/CB-8689) Fix NPE in makeEntryForNativeUri (was affecting file-transfer)
* [CB-8675](https://issues.apache.org/jira/browse/CB-8675) Revert "CB-8351 ios: Use base64EncodedStringWithOptions instead of CordovaLib's class extension"
* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) Updated Readme
* [CB-8659](https://issues.apache.org/jira/browse/CB-8659): ios: 4.0.x Compatibility: Remove use of initWebView method
* Add a cache to speed up AssetFilesystem directory listings
* [CB-8663](https://issues.apache.org/jira/browse/CB-8663) android: Don't notify MediaScanner of private files
* Don't log stacktrace for normal exceptions (e.g. file not found)
* android: Don't use LimitedInputStream when reading entire file (optimization)
* [CB-6428](https://issues.apache.org/jira/browse/CB-6428) android: Add support for directory copies from assets -> filesystem
* android: Add `listChildren()`: Java-consumable version of `readEntriesAtLocalURL()`
* [CB-6428](https://issues.apache.org/jira/browse/CB-6428) android: Add support for file:///android_asset URLs
* [CB-8642](https://issues.apache.org/jira/browse/CB-8642) android: Fix content URIs not working with resolve / copy
* Tweak tests to fail if deleteEntry fails (rather than time out)
* android: Ensure LocalFilesystemURL can only be created with "cdvfile" URLs
* android: Move CordovaResourceApi into Filesystem base class
* android: Use `CordovaResourceApi.mapUriToFile()` rather than own custom logic in ContentFilesystem
* android: Use Uri.parse rather than manual parsing in resolveLocalFileSystemURI
* Tweak test case that failed twice on error rather than just once
* android: Delete invalid JavaDoc (lint errors)
* android: Use CordovaResourceApi rather than FileHelper
* [CB-8032](https://issues.apache.org/jira/browse/CB-8032) - File Plugin - Add nativeURL external method support for CDVFileSystem->makeEntryForPath:isDirectory: (closes #96)
* [CB-8567](https://issues.apache.org/jira/browse/CB-8567) Integrate TravisCI
* [CB-8438](https://issues.apache.org/jira/browse/CB-8438) cordova-plugin-file documentation translation: cordova-plugin-file
* [CB-8538](https://issues.apache.org/jira/browse/CB-8538) Added package.json file
* [CB-7956](https://issues.apache.org/jira/browse/CB-7956) Add cordova-plugin-file support for browser platform
* [CB-8423](https://issues.apache.org/jira/browse/CB-8423) Corrected usage of done() in async tests
* [CB-8459](https://issues.apache.org/jira/browse/CB-8459) Fixes spec 111 failure due to incorrect relative paths handling
* Code cleanup, whitespace
* Added nativeURL property to FileEntry, implemented readAsArrayBuffer and readAsBinaryString
### 1.3.3 (Feb 04, 2015)
* [CB-7927](https://issues.apache.org/jira/browse/CB-7927) Encoding data to bytes instead of chars when writing a file.
* ios: Fix compile warning about implicit int conversion
* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) ios: Use base64EncodedStringWithOptions instead of CordovaLib's class extension
* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) ios: Use argumentForIndex rather than NSArray extension
* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) ios: Use a local copy of valueForKeyIsNumber rather than CordovaLib's version
* windows: Handle url's containing absolute windows path starting with drive letter and colon (encoded as %3A) through root FS
* windows: Rework to use normal url form
* android: refactor: Make Filesystem base class store its own name, rootUri, and rootEntry
* android: Simplify code a bit by making makeEntryForPath not throw JSONException
* [CB-6431](https://issues.apache.org/jira/browse/CB-6431) android: Fix plugin breaking content: URLs
* [CB-7375](https://issues.apache.org/jira/browse/CB-7375) Never create new FileSystem instances (except on windows since they don't implement requestAllFileSystems())
### 1.3.2 (Dec 02, 2014)
* Gets rid of thread block error in File plugin
* [CB-7917](https://issues.apache.org/jira/browse/CB-7917) Made tests file.spec.114 - 116 pass for **Windows** platform
* [CB-7977](https://issues.apache.org/jira/browse/CB-7977) Mention `deviceready` in plugin docs
* [CB-7602](https://issues.apache.org/jira/browse/CB-7602): Fix `isCopyOnItself` logic
* [CB-7700](https://issues.apache.org/jira/browse/CB-7700) cordova-plugin-file documentation translation: cordova-plugin-file
* Use one proxy for both **Windows** and **Windows8** platforms
* [CB-6994](https://issues.apache.org/jira/browse/CB-6994) Fixes result, returned by proxy's write method
* [fxos] update `__format__` to match `pathsPrefix`
* [CB-6994](https://issues.apache.org/jira/browse/CB-6994) Improves merged code to be able to write a File
* Optimize `FileProxy` for **Windows** platforms
* Synchronize changes with **Windows** platform
* Fix function write for big files on **Windows 8**
* Write file in background
* [CB-7487](https://issues.apache.org/jira/browse/CB-7487) **Android** Broadcast file write This allows MTP USB shares to show the file immediately without reboot/manual refresh using 3rd party app.
* [CB-7700](https://issues.apache.org/jira/browse/CB-7700) cordova-plugin-file documentation translation: cordova-plugin-file
* [CB-7571](https://issues.apache.org/jira/browse/CB-7571) Bump version of nested plugin to match parent plugin
### 1.3.1 (Sep 17, 2014)
* [CB-7471](https://issues.apache.org/jira/browse/CB-7471) cordova-plugin-file documentation translation
* [CB-7272](https://issues.apache.org/jira/browse/CB-7272) Replace confusing "r/o" abbreviation with just "r"
* [CB-7423](https://issues.apache.org/jira/browse/CB-7423) encode path before attempting to resolve
* [CB-7375](https://issues.apache.org/jira/browse/CB-7375) Fix the filesystem name in resolveLocalFileSystemUri
* [CB-7445](https://issues.apache.org/jira/browse/CB-7445) [BlackBerry10] resolveLocalFileSystemURI - change DEFAULT_SIZE to MAX_SIZE
* [CB-7458](https://issues.apache.org/jira/browse/CB-7458) [BlackBerry10] resolveLocalFileSystemURL - add filesystem property
* [CB-7445](https://issues.apache.org/jira/browse/CB-7445) [BlackBerry10] Add default file system size to prevent quota exceeded error on initial install
* [CB-7431](https://issues.apache.org/jira/browse/CB-7431) Avoid calling done() twice in file.spec.109 test
* [CB-7413](https://issues.apache.org/jira/browse/CB-7413) Adds support of 'ms-appdata://' URIs
* [CB-7422](https://issues.apache.org/jira/browse/CB-7422) [File Tests] Use proper fileSystem to create fullPath
* [CB-7375](https://issues.apache.org/jira/browse/CB-7375) [Entry] get proper filesystem in Entry
* Amazon related changes.
* [CB-7375](https://issues.apache.org/jira/browse/CB-7375) Remove leading slash statement from condition
* Refactored much of the logic in FileMetadata constructor. Directory.size will return 0
* [CB-7419](https://issues.apache.org/jira/browse/CB-7419) [WP8] Added support to get metada from dir
* [CB-7418](https://issues.apache.org/jira/browse/CB-7418) [DirectoryEntry] Added fullPath variable as part of condition
* [CB-7417](https://issues.apache.org/jira/browse/CB-7417) [File tests] added proper matcher to compare fullPath property
* [CB-7375](https://issues.apache.org/jira/browse/CB-7375) Partial revert to resolve WP8 failures
* Overwrite existing file on getFile when create is true
* [CB-7375](https://issues.apache.org/jira/browse/CB-7375) [CB-6148](https://issues.apache.org/jira/browse/CB-6148): Ensure that return values from copy and move operations reference the correct filesystem
* [CB-6724](https://issues.apache.org/jira/browse/CB-6724) changed style detail on documentation
* Added new js files to amazon-fireos platform.
* Adds Windows platform
* Fixes multiple mobilespec tests errors
* Removed test/tests.js module from main plugin.xml
* [CB-7094](https://issues.apache.org/jira/browse/CB-7094) renamed folder to tests + added nested plugin.xml
* added documentation for manual tests
* [CB-6923](https://issues.apache.org/jira/browse/CB-6923) Adding support to handle relative paths
* Style improvements on Manual tests
* [CB-7094](https://issues.apache.org/jira/browse/CB-7094) Ported File manual tests
### 1.3.0 (Aug 06, 2014)
* **FFOS** Remove unsupported paths from requestAllPaths
* **FFOS** Support for resolve URI, request all paths and local app directory.
* [CB-4263](https://issues.apache.org/jira/browse/CB-4263) set ready state to done before onload
* [CB-7167](https://issues.apache.org/jira/browse/CB-7167) [BlackBerry10] copyTo - return wrapped entry rather than native
* [CB-7167](https://issues.apache.org/jira/browse/CB-7167) [BlackBerry10] Add directory support to getFileMetadata
* [CB-7167](https://issues.apache.org/jira/browse/CB-7167) [BlackBerry10] Fix tests detection of blob support (window.Blob is BlobConstructor object)
* [CB-7161](https://issues.apache.org/jira/browse/CB-7161) [BlackBerry10] Add file system directory paths
* [CB-7093](https://issues.apache.org/jira/browse/CB-7093) Create separate plugin.xml for new-style tests
* [CB-7057](https://issues.apache.org/jira/browse/CB-7057) Docs update: elaborate on what directories are for
* [CB-7093](https://issues.apache.org/jira/browse/CB-7093): Undo the effects of an old bad S&R command
* [CB-7093](https://issues.apache.org/jira/browse/CB-7093): Remove a bunch of unneeded log messages
* [CB-7093](https://issues.apache.org/jira/browse/CB-7093): Add JS module to plugin.xml file for auto-tests
* [CB-7093](https://issues.apache.org/jira/browse/CB-7093) Ported automated file tests
* **WINDOWS** remove extra function closure, not needed
* **WINDOWS** remove check for undefined fail(), it is defined by the proxy and always exists
* **WINDOWS** re-apply readAsBinaryString and readAsArrayBuffer
* **WINDOWS** Moved similar calls to be the same calls, aliased long namespaced functions
* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Updated translations for docs.
* [CB-6571](https://issues.apache.org/jira/browse/CB-6571) Fix getParentForLocalURL to work correctly with directories with trailing '/' (This closes #58)
* UTTypeCopyPreferredTagWithClass returns nil mimetype for css when there is no network
* updated spec links in docs ( en only )
* [CB-6571](https://issues.apache.org/jira/browse/CB-6571) add trailing space it is missing in DirectoryEnty constructor.
* [CB-6980](https://issues.apache.org/jira/browse/CB-6980) Fixing filesystem:null property in Entry
* Add win8 support for readAsBinaryString and readAsArrayBuffer
* [FFOS] Update FileProxy.js
* [CB-6940](https://issues.apache.org/jira/browse/CB-6940): Fixing up commit from dzeims
* [CB-6940](https://issues.apache.org/jira/browse/CB-6940): Android: cleanup try/catch exception handling
* [CB-6940](https://issues.apache.org/jira/browse/CB-6940): `context.getExternal*` methods return null if sdcard isn't in mounted state, causing exceptions that prevent startup from reaching readystate
* Fix mis-handling of filesystem reference in Entry.moveTo ('this' used in closure).
* [CB-6902](https://issues.apache.org/jira/browse/CB-6902): Use File.lastModified rather than .lastModifiedDate
* [CB-6922](https://issues.apache.org/jira/browse/CB-6922): Remove unused getMetadata native code
* [CB-6922](https://issues.apache.org/jira/browse/CB-6922): Use getFileMetadata consistently to get metadata
* changed fullPath to self.rootDocsPath
* [CB-6890](https://issues.apache.org/jira/browse/CB-6890): Fix pluginManager access for 4.0.x branch
### 1.2.1
* [CB-6922](https://issues.apache.org/jira/browse/CB-6922) Fix inconsistent handling of lastModifiedDate and modificationTime
* [CB-285](https://issues.apache.org/jira/browse/CB-285): Document filesystem root properties
### 1.2.0 (Jun 05, 2014)
* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Spanish and French Translations added. Github close #31
* updated this reference to window
* Add missing semicolon (copy & paste error)
* Fix compiler warning about symbol in interface not matching implementation
* Fix sorting order in supported platforms
* ubuntu: increase quota value
* ubuntu: Change FS URL scheme to 'cdvfile'
* ubuntu: Return size with Entry.getMetadata() method
* [CB-6803](https://issues.apache.org/jira/browse/CB-6803) Add license
* Initial implementation for Firefox OS
* Small wording tweaks
* Fixed toURL() toInternalURL() information in the doku
* ios: Don't fail a write of zero-length payload.
* [CB-285](https://issues.apache.org/jira/browse/CB-285) Docs for cordova.file.\*Directory properties
* [CB-285](https://issues.apache.org/jira/browse/CB-285) Add cordova.file.\*Directory properties for iOS & Android
* [CB-3440](https://issues.apache.org/jira/browse/CB-3440) [BlackBerry10] Proxy based implementation
* Fix typo in docs "app-bundle" -> "bundle"
* [CB-6583](https://issues.apache.org/jira/browse/CB-6583) ios: Fix failing to create entry when space in parent path
* [CB-6571](https://issues.apache.org/jira/browse/CB-6571) android: Make DirectoryEntry.toURL() have a trailing /
* [CB-6491](https://issues.apache.org/jira/browse/CB-6491) add CONTRIBUTING.md
* [CB-6525](https://issues.apache.org/jira/browse/CB-6525) android, ios: Allow file: URLs in all APIs. Fixes FileTransfer.download not being called.
* fix the Windows 8 implementation of the getFile method
* Update File.js for typo: lastModifiedData --> lastModifiedDate (closes #38)
* Add error codes.
* [CB-5980](https://issues.apache.org/jira/browse/CB-5980) Updated version and RELEASENOTES.md for release 1.0.0
* Add NOTICE file
* [CB-6114](https://issues.apache.org/jira/browse/CB-6114) Updated version and RELEASENOTES.md for release 1.0.1
* [CB-5980](https://issues.apache.org/jira/browse/CB-5980) Updated version and RELEASENOTES.md for release 1.0.0
### 1.1.0 (Apr 17, 2014)
* [CB-4965](https://issues.apache.org/jira/browse/CB-4965): Remove tests from file plugin
* Android: Allow file:/ URLs
* [CB-6422](https://issues.apache.org/jira/browse/CB-6422): [windows8] use cordova/exec/proxy
* [CB-6249](https://issues.apache.org/jira/browse/CB-6249): [android] Opportunistically resolve content urls to file
* [CB-6394](https://issues.apache.org/jira/browse/CB-6394): [ios, android] Add extra filesystem roots
* [CB-6394](https://issues.apache.org/jira/browse/CB-6394): [ios, android] Fix file resolution for the device root case
* [CB-6394](https://issues.apache.org/jira/browse/CB-6394): [ios] Return ENCODING_ERR when fs name is not valid
* [CB-6393](https://issues.apache.org/jira/browse/CB-6393): Change behaviour of toURL and toNativeURL
* ios: Style: plugin initialization
* ios: Fix handling of file URLs with encoded spaces
* Always use Android's recommended temp file location for temporary file system
* [CB-6352](https://issues.apache.org/jira/browse/CB-6352): Allow FileSystem objects to be serialized to JSON
* [CB-5959](https://issues.apache.org/jira/browse/CB-5959): size is explicitly 0 if not set, file.spec.46&47 are testing the type of size
* [CB-6242](https://issues.apache.org/jira/browse/CB-6242): [BlackBerry10] Add deprecated version of resolveLocalFileSystemURI
* [CB-6242](https://issues.apache.org/jira/browse/CB-6242): [BlackBerry10] add file:/// prefix for toURI / toURL
* [CB-6242](https://issues.apache.org/jira/browse/CB-6242): [BlackBerry10] Polyfill window.requestAnimationFrame for OS < 10.2
* [CB-6242](https://issues.apache.org/jira/browse/CB-6242): [BlackBerry10] Override window.resolveLocalFileSystemURL
* [CB-6212](https://issues.apache.org/jira/browse/CB-6212): [iOS] fix warnings compiled under arm64 64-bit
* ios: Don't cache responses from CDVFile's URLProtocol
* [CB-6199](https://issues.apache.org/jira/browse/CB-6199): [iOS] Fix toNativeURL() not escaping characters properly
* [CB-6148](https://issues.apache.org/jira/browse/CB-6148): Fix cross-filesystem copy and move
* fixed setMetadata() to use the formatted fullPath
* corrected typo which leads to a "comma expression"
* [CB-4952](https://issues.apache.org/jira/browse/CB-4952): ios: Resolve symlinks in file:// URLs
* Add docs about the extraFileSystems preference
* [CB-6460](https://issues.apache.org/jira/browse/CB-6460): Update license headers
### 1.0.1 (Feb 28, 2014)
* [CB-6116](https://issues.apache.org/jira/browse/CB-6116) Fix error where resolveLocalFileSystemURL would fail
* [CB-6106](https://issues.apache.org/jira/browse/CB-6106) Add support for nativeURL attribute on Entry objects
* [CB-6110](https://issues.apache.org/jira/browse/CB-6110) iOS: Fix typo in filesystemPathForURL: method
* Android: Use most specific FS match when resolving file: URIs
* iOS: Update fileSystemURLforLocalPath: to return the most match url.
* Allow third-party plugin registration, and the total count of fs type is not limited to just 4.
* [CB-6097](https://issues.apache.org/jira/browse/CB-6097) Added missing files for amazon-fireos platform. Added onLoad flag to true.
* [CB-6087](https://issues.apache.org/jira/browse/CB-6087) Android, iOS: Load file plugin on startup
* [CB-6013](https://issues.apache.org/jira/browse/CB-6013) BlackBerry10: wrap webkit prefixed called in requestAnimationFrame
* Update plugin writers' documentation
* [CB-6080](https://issues.apache.org/jira/browse/CB-6080) Fix file copy when src and dst are on different local file systems
* [CB-6057](https://issues.apache.org/jira/browse/CB-6057) Add methods for plugins to convert between URLs and paths
* [CB-6050](https://issues.apache.org/jira/browse/CB-6050) Public method for returning a FileEntry from a device file path
* [CB-2432](https://issues.apache.org/jira/browse/CB-2432) [CB-3185](https://issues.apache.org/jira/browse/CB-3185), [CB-5975](https://issues.apache.org/jira/browse/CB-5975): Fix Android handling of content:// URLs
* [CB-6022](https://issues.apache.org/jira/browse/CB-6022) Add upgrade notes to doc
* [CB-5233](https://issues.apache.org/jira/browse/CB-5233) Make asset-library urls work properly on iOS
* [CB-6012](https://issues.apache.org/jira/browse/CB-6012) Preserve query strings on cdvfile:// URLs where necessary
* [CB-6010](https://issues.apache.org/jira/browse/CB-6010) Test properly for presence of URLforFilesystemPath method
* [CB-5959](https://issues.apache.org/jira/browse/CB-5959) Entry.getMetadata should return size attribute
### 1.0.0 (Feb 05, 2014)
* [CB-5974](https://issues.apache.org/jira/browse/CB-5974): Use safe 'Compatibilty' mode by default
* [CB-5915](https://issues.apache.org/jira/browse/CB-5915): [CB-5916](https://issues.apache.org/jira/browse/CB-5916): Reorganize preference code to make defaults possible
* [CB-5974](https://issues.apache.org/jira/browse/CB-5974): Android: Don't allow File operations to continue when not configured
* [CB-5960](https://issues.apache.org/jira/browse/CB-5960): ios: android: Properly handle parent references in getFile/getDirectory
* [ubuntu] adopt to recent changes
* Add default FS root to new FS objects
* [CB-5899](https://issues.apache.org/jira/browse/CB-5899): Make DirectoryReader.readEntries return properly formatted Entry objects
* Add constuctor params to FileUploadResult related to [CB-2421](https://issues.apache.org/jira/browse/CB-2421)
* Fill out filesystem attribute of entities returned from resolveLocalFileSystemURL
* [CB-5916](https://issues.apache.org/jira/browse/CB-5916): Create documents directories if they don't exist
* [CB-5915](https://issues.apache.org/jira/browse/CB-5915): Create documents directories if they don't exist
* [CB-5916](https://issues.apache.org/jira/browse/CB-5916): Android: Fix unfortunate NPE in config check
* [CB-5916](https://issues.apache.org/jira/browse/CB-5916): Android: Add "/files/" to persistent files path
* [CB-5915](https://issues.apache.org/jira/browse/CB-5915): ios: Update config preference (and docs) to match issue
* [CB-5916](https://issues.apache.org/jira/browse/CB-5916): Android: Add config preference for Android persistent storage location
* iOS: Add config preference for iOS persistent storage location
* iOS: Android: Allow third-party plugin registration
* Android: Expose filePlugin getter so that other plugins can register filesystems
* Fix typos in deprecation message
* Add backwards-compatibility shim for file-transfer
* Android: Allow third-party plugin registration
* [CB-5810](https://issues.apache.org/jira/browse/CB-5810) [BlackBerry10] resolve local:/// paths (application assets)
* [CB-5774](https://issues.apache.org/jira/browse/CB-5774): create DirectoryEntry instead of FileEntry
* Initial fix for [CB-5747](https://issues.apache.org/jira/browse/CB-5747)
* Change default FS URL scheme to "cdvfile"
* Android: Properly format content urls
* Android, iOS: Replace "filesystem" protocol string with constant
* Android: Allow absolute paths on Entry.getFile / Entry.getDirectory
* Android: Make clear that getFile takes a path, not just a filename
* [CB-5008](https://issues.apache.org/jira/browse/CB-5008): Rename resolveLocalFileSystemURI to resolveLocalFileSystemURL; deprecate original
* Remove old file reference from plugin.xml
* Android: Refactor File API
* [CB-4899](https://issues.apache.org/jira/browse/CB-4899) [BlackBerry10] Fix resolve directories
* [CB-5602](https://issues.apache.org/jira/browse/CB-5602) Windows8. Fix File Api mobile spec tests
* Android: Better support for content urls and cross-filesystem copy/move ops
* [CB-5699](https://issues.apache.org/jira/browse/CB-5699) [BlackBerry10] Update resolveLocalFileSystemURI implementation
* [CB-5658](https://issues.apache.org/jira/browse/CB-5658) Update license comment formatting of doc/index.md
* [CB-5658](https://issues.apache.org/jira/browse/CB-5658) Add doc.index.md for File plugin.
* [CB-5658](https://issues.apache.org/jira/browse/CB-5658) Delete stale snapshot of plugin docs
* [CB-5403](https://issues.apache.org/jira/browse/CB-5403): Backwards-compatibility with file:// urls where possible
* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Fixes for ContentFilesystem
* Android: Add method for testing backwards-compatibility of filetransfer plugin
* iOS: Add method for testing backwards-compatiblity of filetransfer plugin
* Android: Updates to allow FileTransfer to continue to work
* Android: Clean up unclosed file objects
* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Cleanup
* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Add new Android source files to plugin.xml
* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Move read, write and truncate methods into modules
* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Move copy/move methods into FS modules
* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Move getParent into FS modules
* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Move getmetadata methods into FS modules
* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Move readdir methods into FS modules
* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Move remove methods into FS modules
* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Move getFile into FS modules
* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Start refactoring android code: Modular filesystems, rfs, rlfsurl
* [CB-5407](https://issues.apache.org/jira/browse/CB-5407): Update android JS to use FS urls
* [CB-5405](https://issues.apache.org/jira/browse/CB-5405): Use URL formatting for Entry.toURL
* [CB-5532](https://issues.apache.org/jira/browse/CB-5532) Fix
* Log file path for File exceptions.
* Partial fix for iOS File compatibility with previous fileTransfer plugin
* [CB-5532](https://issues.apache.org/jira/browse/CB-5532) WP8. Add binary data support to FileWriter
* [CB-5531](https://issues.apache.org/jira/browse/CB-5531) WP8. File Api readAsText incorrectly handles position args
* Added ubuntu platform support
* Added amazon-fireos platform support
* [CB-5118](https://issues.apache.org/jira/browse/CB-5118) [BlackBerry10] Add check for undefined error handler
* [CB-5406](https://issues.apache.org/jira/browse/CB-5406): Extend public API for dependent plugins
* [CB-5403](https://issues.apache.org/jira/browse/CB-5403): Bump File plugin major version
* [CB-5406](https://issues.apache.org/jira/browse/CB-5406): Split iOS file plugin into modules
* [CB-5406](https://issues.apache.org/jira/browse/CB-5406): Factor out filesystem providers in iOS
* [CB-5408](https://issues.apache.org/jira/browse/CB-5408): Add handler for filesystem:// urls
* [CB-5406](https://issues.apache.org/jira/browse/CB-5406): Update iOS native code to use filesystem URLs internally
* [CB-5405](https://issues.apache.org/jira/browse/CB-5405): Update JS code to use URLs exclusively
* [CB-4816](https://issues.apache.org/jira/browse/CB-4816) Fix file creation outside sandbox for BB10
### 0.2.5 (Oct 28, 2013)
* [CB-5129](https://issues.apache.org/jira/browse/CB-5129): Add a consistent filesystem attribute to FileEntry and DirectoryEntry objects
* [CB-5128](https://issues.apache.org/jira/browse/CB-5128): added repo + issue tag to plugin.xml for file plugin
* [CB-5015](https://issues.apache.org/jira/browse/CB-5015) [BlackBerry10] Add missing dependency for File.slice
* [CB-5010](https://issues.apache.org/jira/browse/CB-5010) Incremented plugin version on dev branch.
### 0.2.4 (Oct 9, 2013)
* [CB-5020](https://issues.apache.org/jira/browse/CB-5020) - File plugin should execute on a separate thread
* [CB-4915](https://issues.apache.org/jira/browse/CB-4915) Incremented plugin version on dev branch.
* [CB-4504](https://issues.apache.org/jira/browse/CB-4504): Updating FileUtils.java to compensate for Java porting failures in the Android SDK. This fails because Java knows nothing about android_asset not being an actual filesystem
### 0.2.3 (Sept 25, 2013)
* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) bumping&resetting version
* [CB-4903](https://issues.apache.org/jira/browse/CB-4903) File Plugin not loading Windows8
* [CB-4903](https://issues.apache.org/jira/browse/CB-4903) File Plugin not loading Windows8
* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming references
* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming org.apache.cordova.core.file to org.apache.cordova.file
* Rename CHANGELOG.md -> RELEASENOTES.md
* [CB-4771](https://issues.apache.org/jira/browse/CB-4771) Expose TEMPORARY and PERSISTENT constants on window.
* Fix compiler/lint warnings
* [CB-4764](https://issues.apache.org/jira/browse/CB-4764) Move DirectoryManager.java into file plugin
* [CB-4763](https://issues.apache.org/jira/browse/CB-4763) Copy FileHelper.java into the plugin.
* [CB-2901](https://issues.apache.org/jira/browse/CB-2901) [BlackBerry10] Automatically unsandbox filesystem if path is not in app sandbox
* [CB-4752](https://issues.apache.org/jira/browse/CB-4752) Incremented plugin version on dev branch.
### 0.2.1 (Sept 5, 2013)
* [CB-4656](https://issues.apache.org/jira/browse/CB-4656) Don't add newlines in data urls within readAsDataUrl.
* [CB-4514](https://issues.apache.org/jira/browse/CB-4514) Making DirectoryCopy Recursive
* [iOS] Simplify the code in resolveLocalFileSystemURI

View File

@@ -0,0 +1,120 @@
<!---
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
Notes for plugin developers
===========================
These notes are primarily intended for Android and iOS developers who want to write plugins which interface with the file system using the File plugin.
Working with Cordova file system URLs
-------------------------------------
Since version 1.0.0, this plugin has used URLs with a `cdvfile` scheme for all communication over the bridge, rather than exposing raw device file system paths to JavaScript.
On the JavaScript side, this means that FileEntry and DirectoryEntry objects have a fullPath attribute which is relative to the root of the HTML file system. If your plugin's JavaScript API accepts a FileEntry or DirectoryEntry object, you should call `.toURL()` on that object before passing it across the bridge to native code.
### Converting cdvfile:// URLs to fileystem paths
Plugins which need to write to the filesystem may want to convert a received file system URL to an actual filesystem location. There are multiple ways of doing this, depending on the native platform.
It is important to remember that not all `cdvfile://` URLs are mappable to real files on the device. Some URLs can refer to assets on device which are not represented by files, or can even refer to remote resources. Because of these possibilities, plugins should always test whether they get a meaningful result back when trying to convert URLs to paths.
#### Android
On Android, the simplest method to convert a `cdvfile://` URL to a filesystem path is to use `org.apache.cordova.CordovaResourceApi`. `CordovaResourceApi` has several methods which can handle `cdvfile://` URLs:
// webView is a member of the Plugin class
CordovaResourceApi resourceApi = webView.getResourceApi();
// Obtain a file:/// URL representing this file on the device,
// or the same URL unchanged if it cannot be mapped to a file
Uri fileURL = resourceApi.remapUri(Uri.parse(cdvfileURL));
It is also possible to use the File plugin directly:
import org.apache.cordova.file.FileUtils;
import org.apache.cordova.file.FileSystem;
import java.net.MalformedURLException;
// Get the File plugin from the plugin manager
FileUtils filePlugin = (FileUtils)webView.pluginManager.getPlugin("File");
// Given a URL, get a path for it
try {
String path = filePlugin.filesystemPathForURL(cdvfileURL);
} catch (MalformedURLException e) {
// The filesystem url wasn't recognized
}
To convert from a path to a `cdvfile://` URL:
import org.apache.cordova.file.LocalFilesystemURL;
// Get a LocalFilesystemURL object for a device path,
// or null if it cannot be represented as a cdvfile URL.
LocalFilesystemURL url = filePlugin.filesystemURLforLocalPath(path);
// Get the string representation of the URL object
String cdvfileURL = url.toString();
If your plugin creates a file, and you want to return a FileEntry object for it, use the File plugin:
// Return a JSON structure suitable for returning to JavaScript,
// or null if this file is not representable as a cdvfile URL.
JSONObject entry = filePlugin.getEntryForFile(file);
#### iOS
Cordova on iOS does not use the same `CordovaResourceApi` concept as Android. On iOS, you should use the File plugin to convert between URLs and filesystem paths.
// Get a CDVFilesystem URL object from a URL string
CDVFilesystemURL* url = [CDVFilesystemURL fileSystemURLWithString:cdvfileURL];
// Get a path for the URL object, or nil if it cannot be mapped to a file
NSString* path = [filePlugin filesystemPathForURL:url];
// Get a CDVFilesystem URL object for a device path, or
// nil if it cannot be represented as a cdvfile URL.
CDVFilesystemURL* url = [filePlugin fileSystemURLforLocalPath:path];
// Get the string representation of the URL object
NSString* cdvfileURL = [url absoluteString];
If your plugin creates a file, and you want to return a FileEntry object for it, use the File plugin:
// Get a CDVFilesystem URL object for a device path, or
// nil if it cannot be represented as a cdvfile URL.
CDVFilesystemURL* url = [filePlugin fileSystemURLforLocalPath:path];
// Get a structure to return to JavaScript
NSDictionary* entry = [filePlugin makeEntryForLocalURL:url]
#### JavaScript
In JavaScript, to get a `cdvfile://` URL from a FileEntry or DirectoryEntry object, simply call `.toURL()` on it:
var cdvfileURL = entry.toURL();
In plugin response handlers, to convert from a returned FileEntry structure to an actual Entry object, your handler code should import the File plugin and create a new object:
// create appropriate Entry object
var entry;
if (entryStruct.isDirectory) {
entry = new DirectoryEntry(entryStruct.name, entryStruct.fullPath, new FileSystem(entryStruct.filesystemName));
} else {
entry = new FileEntry(entryStruct.name, entryStruct.fullPath, new FileSystem(entryStruct.filesystemName));
}

View File

@@ -0,0 +1,50 @@
{
"name": "cordova-plugin-file",
"version": "7.0.0",
"description": "Cordova File Plugin",
"types": "./types/index.d.ts",
"cordova": {
"id": "cordova-plugin-file",
"platforms": [
"android",
"browser",
"ios",
"osx",
"windows"
]
},
"repository": "github:apache/cordova-plugin-file",
"bugs": "https://github.com/apache/cordova-plugin-file/issues",
"keywords": [
"cordova",
"file",
"ecosystem:cordova",
"cordova-android",
"cordova-browser",
"cordova-ios",
"cordova-osx",
"cordova-windows"
],
"scripts": {
"test": "npm run lint",
"lint": "eslint ."
},
"author": "Apache Software Foundation",
"license": "Apache-2.0",
"engines": {
"cordovaDependencies": {
"5.0.0": {
"cordova-android": ">=6.3.0"
},
"7.0.0": {
"cordova-android": ">=10.0.0"
},
"8.0.0": {
"cordova": ">100"
}
}
},
"devDependencies": {
"@cordova/eslint-config": "^3.0.0"
}
}

Some files were not shown because too many files have changed in this diff Show More