暫無描述

FileProxy.js 44KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190
  1. /*
  2. *
  3. * Licensed to the Apache Software Foundation (ASF) under one
  4. * or more contributor license agreements. See the NOTICE file
  5. * distributed with this work for additional information
  6. * regarding copyright ownership. The ASF licenses this file
  7. * to you under the Apache License, Version 2.0 (the
  8. * "License"); you may not use this file except in compliance
  9. * with the License. You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing,
  14. * software distributed under the License is distributed on an
  15. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  16. * KIND, either express or implied. See the License for the
  17. * specific language governing permissions and limitations
  18. * under the License.
  19. *
  20. */
  21. /* global Windows, WinJS, MSApp */
  22. var File = require('./File');
  23. var FileError = require('./FileError');
  24. var Flags = require('./Flags');
  25. var FileSystem = require('./FileSystem');
  26. var LocalFileSystem = require('./LocalFileSystem');
  27. var utils = require('cordova/utils');
  28. function Entry (isFile, isDirectory, name, fullPath, filesystemName, nativeURL) {
  29. this.isFile = !!isFile;
  30. this.isDirectory = !!isDirectory;
  31. this.name = name || '';
  32. this.fullPath = fullPath || '';
  33. this.filesystemName = filesystemName || null;
  34. this.nativeURL = nativeURL || null;
  35. }
  36. var FileEntry = function (name, fullPath, filesystemName, nativeURL) {
  37. FileEntry.__super__.constructor.apply(this, [true, false, name, fullPath, filesystemName, nativeURL]);
  38. };
  39. utils.extend(FileEntry, Entry);
  40. var DirectoryEntry = function (name, fullPath, filesystemName, nativeURL) {
  41. DirectoryEntry.__super__.constructor.call(this, false, true, name, fullPath, filesystemName, nativeURL);
  42. };
  43. utils.extend(DirectoryEntry, Entry);
  44. var getFolderFromPathAsync = Windows.Storage.StorageFolder.getFolderFromPathAsync;
  45. var getFileFromPathAsync = Windows.Storage.StorageFile.getFileFromPathAsync;
  46. function writeBytesAsync (storageFile, data, position) {
  47. return storageFile.openAsync(Windows.Storage.FileAccessMode.readWrite)
  48. .then(function (output) {
  49. output.seek(position);
  50. var dataWriter = new Windows.Storage.Streams.DataWriter(output);
  51. dataWriter.writeBytes(data);
  52. return dataWriter.storeAsync().then(function (size) {
  53. output.size = position + size;
  54. return dataWriter.flushAsync().then(function () {
  55. output.close();
  56. return size;
  57. });
  58. });
  59. });
  60. }
  61. function writeTextAsync (storageFile, data, position) {
  62. return storageFile.openAsync(Windows.Storage.FileAccessMode.readWrite)
  63. .then(function (output) {
  64. output.seek(position);
  65. var dataWriter = new Windows.Storage.Streams.DataWriter(output);
  66. dataWriter.writeString(data);
  67. return dataWriter.storeAsync().then(function (size) {
  68. output.size = position + size;
  69. return dataWriter.flushAsync().then(function () {
  70. output.close();
  71. return size;
  72. });
  73. });
  74. });
  75. }
  76. function writeBlobAsync (storageFile, data, position) {
  77. return storageFile.openAsync(Windows.Storage.FileAccessMode.readWrite)
  78. .then(function (output) {
  79. output.seek(position);
  80. var dataSize = data.size;
  81. var input = (data.detachStream || data.msDetachStream).call(data);
  82. // Copy the stream from the blob to the File stream
  83. return Windows.Storage.Streams.RandomAccessStream.copyAsync(input, output)
  84. .then(function () {
  85. output.size = position + dataSize;
  86. return output.flushAsync().then(function () {
  87. input.close();
  88. output.close();
  89. return dataSize;
  90. });
  91. });
  92. });
  93. }
  94. function writeArrayBufferAsync (storageFile, data, position) {
  95. return writeBlobAsync(storageFile, new Blob([data]), position); // eslint-disable-line no-undef
  96. }
  97. function cordovaPathToNative (path) {
  98. // turn / into \\
  99. var cleanPath = path.replace(/\//g, '\\');
  100. // turn \\ into \
  101. cleanPath = cleanPath.replace(/\\+/g, '\\');
  102. return cleanPath;
  103. }
  104. function nativePathToCordova (path) {
  105. var cleanPath = path.replace(/\\/g, '/');
  106. return cleanPath;
  107. }
  108. var driveRE = new RegExp('^[/]*([A-Z]:)');
  109. var invalidNameRE = /[\\?*|"<>:]/;
  110. function validName (name) {
  111. return !invalidNameRE.test(name.replace(driveRE, ''));
  112. }
  113. function sanitize (path) {
  114. var slashesRE = new RegExp('/{2,}', 'g');
  115. var components = path.replace(slashesRE, '/').split(/\/+/);
  116. // Remove double dots, use old school array iteration instead of RegExp
  117. // since it is impossible to debug them
  118. for (var index = 0; index < components.length; ++index) {
  119. if (components[index] === '..') {
  120. components.splice(index, 1);
  121. if (index > 0) {
  122. // if we're not in the start of array then remove preceeding path component,
  123. // In case if relative path points above the root directory, just ignore double dots
  124. // See file.spec.111 should not traverse above above the root directory for test case
  125. components.splice(index - 1, 1);
  126. --index;
  127. }
  128. }
  129. }
  130. return components.join('/');
  131. }
  132. var WinFS = function (name, root) {
  133. this.winpath = root.winpath;
  134. if (this.winpath && !/\/$/.test(this.winpath)) {
  135. this.winpath += '/';
  136. }
  137. this.makeNativeURL = function (path) {
  138. // CB-11848: This RE supposed to match all leading slashes in sanitized path.
  139. // Removing leading slash to avoid duplicating because this.root.nativeURL already has trailing slash
  140. var regLeadingSlashes = /^\/*/;
  141. var sanitizedPath = sanitize(path.replace(':', '%3A')).replace(regLeadingSlashes, '');
  142. return FileSystem.encodeURIPath(this.root.nativeURL + sanitizedPath);
  143. };
  144. root.fullPath = '/';
  145. if (!root.nativeURL) { root.nativeURL = 'file://' + sanitize(this.winpath + root.fullPath).replace(':', '%3A'); }
  146. WinFS.__super__.constructor.call(this, name, root);
  147. };
  148. utils.extend(WinFS, FileSystem);
  149. WinFS.prototype.__format__ = function (fullPath) {
  150. var path = sanitize('/' + this.name + (fullPath[0] === '/' ? '' : '/') + FileSystem.encodeURIPath(fullPath));
  151. return 'cdvfile://localhost' + path;
  152. };
  153. var windowsPaths = {
  154. dataDirectory: 'ms-appdata:///local/',
  155. cacheDirectory: 'ms-appdata:///temp/',
  156. tempDirectory: 'ms-appdata:///temp/',
  157. syncedDataDirectory: 'ms-appdata:///roaming/',
  158. applicationDirectory: 'ms-appx:///',
  159. applicationStorageDirectory: 'ms-appx:///'
  160. };
  161. var AllFileSystems;
  162. function getAllFS () {
  163. if (!AllFileSystems) {
  164. AllFileSystems = {
  165. 'persistent':
  166. Object.freeze(new WinFS('persistent', {
  167. name: 'persistent',
  168. nativeURL: 'ms-appdata:///local',
  169. winpath: nativePathToCordova(Windows.Storage.ApplicationData.current.localFolder.path)
  170. })),
  171. 'temporary':
  172. Object.freeze(new WinFS('temporary', {
  173. name: 'temporary',
  174. nativeURL: 'ms-appdata:///temp',
  175. winpath: nativePathToCordova(Windows.Storage.ApplicationData.current.temporaryFolder.path)
  176. })),
  177. 'application':
  178. Object.freeze(new WinFS('application', {
  179. name: 'application',
  180. nativeURL: 'ms-appx:///',
  181. winpath: nativePathToCordova(Windows.ApplicationModel.Package.current.installedLocation.path)
  182. })),
  183. 'root':
  184. Object.freeze(new WinFS('root', {
  185. name: 'root',
  186. // nativeURL: 'file:///'
  187. winpath: ''
  188. }))
  189. };
  190. }
  191. return AllFileSystems;
  192. }
  193. function getFS (name) {
  194. return getAllFS()[name];
  195. }
  196. FileSystem.prototype.__format__ = function (fullPath) {
  197. return getFS(this.name).__format__(fullPath);
  198. };
  199. require('./fileSystems').getFs = function (name, callback) {
  200. setTimeout(function () { callback(getFS(name)); });
  201. };
  202. function getFilesystemFromPath (path) {
  203. var res;
  204. var allfs = getAllFS();
  205. Object.keys(allfs).some(function (fsn) {
  206. var fs = allfs[fsn];
  207. if (path.indexOf(fs.winpath) === 0) { res = fs; }
  208. return res;
  209. });
  210. return res;
  211. }
  212. var msapplhRE = new RegExp('^ms-appdata://localhost/');
  213. function pathFromURL (url) {
  214. url = url.replace(msapplhRE, 'ms-appdata:///');
  215. var path = decodeURIComponent(url);
  216. // support for file name with parameters
  217. if (/\?/g.test(path)) {
  218. path = String(path).split('?')[0];
  219. }
  220. if (path.indexOf('file:/') === 0) {
  221. if (path.indexOf('file://') !== 0) {
  222. url = 'file:///' + url.substr(6);
  223. }
  224. }
  225. ['file://', 'ms-appdata:///', 'ms-appx://', 'cdvfile://localhost/'].every(function (p) {
  226. if (path.indexOf(p) !== 0) { return true; }
  227. var thirdSlash = path.indexOf('/', p.length);
  228. if (thirdSlash < 0) {
  229. path = '';
  230. } else {
  231. path = sanitize(path.substr(thirdSlash));
  232. }
  233. });
  234. return path.replace(driveRE, '$1');
  235. }
  236. function getFilesystemFromURL (url) {
  237. url = url.replace(msapplhRE, 'ms-appdata:///');
  238. var res;
  239. if (url.indexOf('file:/') === 0) { res = getFilesystemFromPath(pathFromURL(url)); } else {
  240. var allfs = getAllFS();
  241. Object.keys(allfs).every(function (fsn) {
  242. var fs = allfs[fsn];
  243. if (url.indexOf(fs.root.nativeURL) === 0 ||
  244. url.indexOf('cdvfile://localhost/' + fs.name + '/') === 0) {
  245. res = fs;
  246. return false;
  247. }
  248. return true;
  249. });
  250. }
  251. return res;
  252. }
  253. function getFsPathForWinPath (fs, wpath) {
  254. var path = nativePathToCordova(wpath);
  255. if (path.indexOf(fs.winpath) !== 0) { return null; }
  256. return path.replace(fs.winpath, '/');
  257. }
  258. var WinError = {
  259. invalidArgument: -2147024809,
  260. fileNotFound: -2147024894,
  261. accessDenied: -2147024891
  262. };
  263. function openPath (path, ops) {
  264. ops = ops || {};
  265. return new WinJS.Promise(function (complete, failed) {
  266. getFileFromPathAsync(path).done(
  267. function (file) {
  268. complete({file: file});
  269. },
  270. function (err) {
  271. if (err.number !== WinError.fileNotFound && err.number !== WinError.invalidArgument) { failed(FileError.NOT_READABLE_ERR); }
  272. getFolderFromPathAsync(path)
  273. .done(
  274. function (dir) {
  275. if (!ops.getContent) { complete({folder: dir}); } else {
  276. WinJS.Promise.join({
  277. files: dir.getFilesAsync(),
  278. folders: dir.getFoldersAsync()
  279. }).done(
  280. function (a) {
  281. complete({
  282. folder: dir,
  283. files: a.files,
  284. folders: a.folders
  285. });
  286. },
  287. function (err) { // eslint-disable-line handle-callback-err
  288. failed(FileError.NOT_READABLE_ERR);
  289. }
  290. );
  291. }
  292. },
  293. function (err) {
  294. if (err.number === WinError.fileNotFound || err.number === WinError.invalidArgument) { complete({}); } else { failed(FileError.NOT_READABLE_ERR); }
  295. }
  296. );
  297. }
  298. );
  299. });
  300. }
  301. function copyFolder (src, dst, name) {
  302. name = name || src.name;
  303. return new WinJS.Promise(function (complete, failed) {
  304. WinJS.Promise.join({
  305. fld: dst.createFolderAsync(name, Windows.Storage.CreationCollisionOption.openIfExists),
  306. files: src.getFilesAsync(),
  307. folders: src.getFoldersAsync()
  308. }).done(
  309. function (the) {
  310. if (!(the.files.length || the.folders.length)) {
  311. complete();
  312. return;
  313. }
  314. var todo = the.files.length;
  315. var copyfolders = function () {
  316. if (!(todo--)) {
  317. complete();
  318. return;
  319. }
  320. copyFolder(the.folders[todo], dst)
  321. .done(function () { copyfolders(); }, failed);
  322. };
  323. var copyfiles = function () {
  324. if (!(todo--)) {
  325. todo = the.folders.length;
  326. copyfolders();
  327. return;
  328. }
  329. the.files[todo].copyAsync(the.fld)
  330. .done(function () { copyfiles(); }, failed);
  331. };
  332. copyfiles();
  333. },
  334. failed
  335. );
  336. });
  337. }
  338. function moveFolder (src, dst, name) {
  339. name = name || src.name;
  340. return new WinJS.Promise(function (complete, failed) {
  341. WinJS.Promise.join({
  342. fld: dst.createFolderAsync(name, Windows.Storage.CreationCollisionOption.openIfExists),
  343. files: src.getFilesAsync(),
  344. folders: src.getFoldersAsync()
  345. }).done(
  346. function (the) {
  347. if (!(the.files.length || the.folders.length)) {
  348. complete();
  349. return;
  350. }
  351. var todo = the.files.length;
  352. var movefolders = function () {
  353. if (!(todo--)) {
  354. src.deleteAsync().done(complete, failed);
  355. return;
  356. }
  357. moveFolder(the.folders[todo], the.fld)
  358. .done(movefolders, failed);
  359. };
  360. var movefiles = function () {
  361. if (!(todo--)) {
  362. todo = the.folders.length;
  363. movefolders();
  364. return;
  365. }
  366. the.files[todo].moveAsync(the.fld)
  367. .done(function () { movefiles(); }, failed);
  368. };
  369. movefiles();
  370. },
  371. failed
  372. );
  373. });
  374. }
  375. function transport (success, fail, args, ops) { // ["fullPath","parent", "newName"]
  376. var src = args[0];
  377. var parent = args[1];
  378. var name = args[2];
  379. var srcFS = getFilesystemFromURL(src);
  380. var dstFS = getFilesystemFromURL(parent);
  381. var srcPath = pathFromURL(src);
  382. var dstPath = pathFromURL(parent);
  383. if (!(srcFS && dstFS && validName(name))) {
  384. fail(FileError.ENCODING_ERR);
  385. return;
  386. }
  387. var srcWinPath = cordovaPathToNative(sanitize(srcFS.winpath + srcPath));
  388. var dstWinPath = cordovaPathToNative(sanitize(dstFS.winpath + dstPath));
  389. var tgtFsPath = sanitize(dstPath + '/' + name);
  390. var tgtWinPath = cordovaPathToNative(sanitize(dstFS.winpath + dstPath + '/' + name));
  391. if (srcWinPath === dstWinPath || srcWinPath === tgtWinPath) {
  392. fail(FileError.INVALID_MODIFICATION_ERR);
  393. return;
  394. }
  395. WinJS.Promise.join({
  396. src: openPath(srcWinPath),
  397. dst: openPath(dstWinPath),
  398. tgt: openPath(tgtWinPath, {getContent: true})
  399. })
  400. .done(
  401. function (the) {
  402. if ((!the.dst.folder) || !(the.src.folder || the.src.file)) {
  403. fail(FileError.NOT_FOUND_ERR);
  404. return;
  405. }
  406. if ((the.src.folder && the.tgt.file) ||
  407. (the.src.file && the.tgt.folder) ||
  408. (the.tgt.folder && (the.tgt.files.length || the.tgt.folders.length))) {
  409. fail(FileError.INVALID_MODIFICATION_ERR);
  410. return;
  411. }
  412. if (the.src.file) {
  413. ops.fileOp(the.src.file, the.dst.folder, name, Windows.Storage.NameCollisionOption.replaceExisting)
  414. .done(
  415. function (storageFile) {
  416. success(new FileEntry(
  417. name,
  418. tgtFsPath,
  419. dstFS.name,
  420. dstFS.makeNativeURL(tgtFsPath)
  421. ));
  422. },
  423. function (err) { // eslint-disable-line handle-callback-err
  424. fail(FileError.INVALID_MODIFICATION_ERR);
  425. }
  426. );
  427. } else {
  428. ops.folderOp(the.src.folder, the.dst.folder, name).done(
  429. function () {
  430. success(new DirectoryEntry(
  431. name,
  432. tgtFsPath,
  433. dstFS.name,
  434. dstFS.makeNativeURL(tgtFsPath)
  435. ));
  436. },
  437. function () {
  438. fail(FileError.INVALID_MODIFICATION_ERR);
  439. }
  440. );
  441. }
  442. },
  443. function (err) { // eslint-disable-line handle-callback-err
  444. fail(FileError.INVALID_MODIFICATION_ERR);
  445. }
  446. );
  447. }
  448. module.exports = {
  449. requestAllFileSystems: function () {
  450. return getAllFS();
  451. },
  452. requestAllPaths: function (success) {
  453. success(windowsPaths);
  454. },
  455. getFileMetadata: function (success, fail, args) {
  456. module.exports.getMetadata(success, fail, args);
  457. },
  458. getMetadata: function (success, fail, args) {
  459. var fs = getFilesystemFromURL(args[0]);
  460. var path = pathFromURL(args[0]);
  461. if (!fs || !validName(path)) {
  462. fail(FileError.ENCODING_ERR);
  463. return;
  464. }
  465. var fullPath = cordovaPathToNative(fs.winpath + path);
  466. var getMetadataForFile = function (storageFile) {
  467. storageFile.getBasicPropertiesAsync().then(
  468. function (basicProperties) {
  469. success(new File(storageFile.name, storageFile.path, storageFile.fileType, basicProperties.dateModified, basicProperties.size));
  470. }, function () {
  471. fail(FileError.NOT_READABLE_ERR);
  472. }
  473. );
  474. };
  475. var getMetadataForFolder = function (storageFolder) {
  476. storageFolder.getBasicPropertiesAsync().then(
  477. function (basicProperties) {
  478. var metadata = {
  479. size: basicProperties.size,
  480. lastModifiedDate: basicProperties.dateModified
  481. };
  482. success(metadata);
  483. },
  484. function () {
  485. fail(FileError.NOT_READABLE_ERR);
  486. }
  487. );
  488. };
  489. getFileFromPathAsync(fullPath).then(getMetadataForFile,
  490. function () {
  491. getFolderFromPathAsync(fullPath).then(getMetadataForFolder,
  492. function () {
  493. fail(FileError.NOT_FOUND_ERR);
  494. }
  495. );
  496. }
  497. );
  498. },
  499. getParent: function (win, fail, args) { // ["fullPath"]
  500. var fs = getFilesystemFromURL(args[0]);
  501. var path = pathFromURL(args[0]);
  502. if (!fs || !validName(path)) {
  503. fail(FileError.ENCODING_ERR);
  504. return;
  505. }
  506. if (!path || (new RegExp('/[^/]*/?$')).test(path)) {
  507. win(new DirectoryEntry(fs.root.name, fs.root.fullPath, fs.name, fs.makeNativeURL(fs.root.fullPath)));
  508. return;
  509. }
  510. var parpath = path.replace(new RegExp('/[^/]+/?$', 'g'), '');
  511. var parname = path.substr(parpath.length);
  512. var fullPath = cordovaPathToNative(fs.winpath + parpath);
  513. var result = new DirectoryEntry(parname, parpath, fs.name, fs.makeNativeURL(parpath));
  514. getFolderFromPathAsync(fullPath).done(
  515. function () { win(result); },
  516. function () { fail(FileError.INVALID_STATE_ERR); }
  517. );
  518. },
  519. readAsText: function (win, fail, args) {
  520. var url = args[0];
  521. var enc = args[1];
  522. var startPos = args[2];
  523. var endPos = args[3];
  524. var fs = getFilesystemFromURL(url);
  525. var path = pathFromURL(url);
  526. if (!fs) {
  527. fail(FileError.ENCODING_ERR);
  528. return;
  529. }
  530. var wpath = cordovaPathToNative(sanitize(fs.winpath + path));
  531. var encoding = Windows.Storage.Streams.UnicodeEncoding.utf8;
  532. if (enc === 'Utf16LE' || enc === 'utf16LE') {
  533. encoding = Windows.Storage.Streams.UnicodeEncoding.utf16LE;
  534. } else if (enc === 'Utf16BE' || enc === 'utf16BE') {
  535. encoding = Windows.Storage.Streams.UnicodeEncoding.utf16BE;
  536. }
  537. getFileFromPathAsync(wpath).then(function (file) {
  538. return file.openReadAsync();
  539. }).then(function (stream) {
  540. startPos = (startPos < 0) ? Math.max(stream.size + startPos, 0) : Math.min(stream.size, startPos);
  541. endPos = (endPos < 0) ? Math.max(endPos + stream.size, 0) : Math.min(stream.size, endPos);
  542. stream.seek(startPos);
  543. var readSize = endPos - startPos;
  544. var buffer = new Windows.Storage.Streams.Buffer(readSize);
  545. return stream.readAsync(buffer, readSize, Windows.Storage.Streams.InputStreamOptions.none);
  546. }).done(function (buffer) {
  547. try {
  548. win(Windows.Security.Cryptography.CryptographicBuffer.convertBinaryToString(encoding, buffer));
  549. } catch (e) {
  550. fail(FileError.ENCODING_ERR);
  551. }
  552. }, function () {
  553. fail(FileError.NOT_FOUND_ERR);
  554. });
  555. },
  556. readAsBinaryString: function (win, fail, args) {
  557. var url = args[0];
  558. var startPos = args[1];
  559. var endPos = args[2];
  560. var fs = getFilesystemFromURL(url);
  561. var path = pathFromURL(url);
  562. if (!fs) {
  563. fail(FileError.ENCODING_ERR);
  564. return;
  565. }
  566. var wpath = cordovaPathToNative(sanitize(fs.winpath + path));
  567. getFileFromPathAsync(wpath).then(
  568. function (storageFile) {
  569. Windows.Storage.FileIO.readBufferAsync(storageFile).done(
  570. function (buffer) {
  571. var dataReader = Windows.Storage.Streams.DataReader.fromBuffer(buffer);
  572. // var fileContent = dataReader.readString(buffer.length);
  573. var byteArray = new Uint8Array(buffer.length);
  574. var byteString = '';
  575. dataReader.readBytes(byteArray);
  576. dataReader.close();
  577. for (var i = 0; i < byteArray.length; i++) {
  578. var charByte = byteArray[i];
  579. // var charRepresentation = charByte <= 127 ? String.fromCharCode(charByte) : charByte.toString(16);
  580. var charRepresentation = String.fromCharCode(charByte);
  581. byteString += charRepresentation;
  582. }
  583. win(byteString.slice(startPos, endPos));
  584. }
  585. );
  586. }, function () {
  587. fail(FileError.NOT_FOUND_ERR);
  588. }
  589. );
  590. },
  591. readAsArrayBuffer: function (win, fail, args) {
  592. var url = args[0];
  593. var fs = getFilesystemFromURL(url);
  594. var path = pathFromURL(url);
  595. if (!fs) {
  596. fail(FileError.ENCODING_ERR);
  597. return;
  598. }
  599. var wpath = cordovaPathToNative(sanitize(fs.winpath + path));
  600. getFileFromPathAsync(wpath).then(
  601. function (storageFile) {
  602. var blob = MSApp.createFileFromStorageFile(storageFile);
  603. var url = URL.createObjectURL(blob, { oneTimeOnly: true }); // eslint-disable-line no-undef
  604. var xhr = new XMLHttpRequest(); // eslint-disable-line no-undef
  605. xhr.open('GET', url, true);
  606. xhr.responseType = 'arraybuffer';
  607. xhr.onload = function () {
  608. var resultArrayBuffer = xhr.response;
  609. // get start and end position of bytes in buffer to be returned
  610. var startPos = args[1] || 0;
  611. var endPos = args[2] || resultArrayBuffer.length;
  612. // if any of them is specified, we'll slice output array
  613. if (startPos !== 0 || endPos !== resultArrayBuffer.length) {
  614. // slice method supported only on Windows 8.1, so we need to check if it's available
  615. // see http://msdn.microsoft.com/en-us/library/ie/dn641192(v=vs.94).aspx
  616. if (resultArrayBuffer.slice) {
  617. resultArrayBuffer = resultArrayBuffer.slice(startPos, endPos);
  618. } else {
  619. // if slice isn't available, we'll use workaround method
  620. var tempArray = new Uint8Array(resultArrayBuffer);
  621. var resBuffer = new ArrayBuffer(endPos - startPos);
  622. var resArray = new Uint8Array(resBuffer);
  623. for (var i = 0; i < resArray.length; i++) {
  624. resArray[i] = tempArray[i + startPos];
  625. }
  626. resultArrayBuffer = resBuffer;
  627. }
  628. }
  629. win(resultArrayBuffer);
  630. };
  631. xhr.send();
  632. }, function () {
  633. fail(FileError.NOT_FOUND_ERR);
  634. }
  635. );
  636. },
  637. readAsDataURL: function (win, fail, args) {
  638. var url = args[0];
  639. var fs = getFilesystemFromURL(url);
  640. var path = pathFromURL(url);
  641. if (!fs) {
  642. fail(FileError.ENCODING_ERR);
  643. return;
  644. }
  645. var wpath = cordovaPathToNative(sanitize(fs.winpath + path));
  646. getFileFromPathAsync(wpath).then(
  647. function (storageFile) {
  648. Windows.Storage.FileIO.readBufferAsync(storageFile).done(
  649. function (buffer) {
  650. var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer);
  651. // the method encodeToBase64String will add "77u/" as a prefix, so we should remove it
  652. if (String(strBase64).substr(0, 4) === '77u/') {
  653. strBase64 = strBase64.substr(4);
  654. }
  655. var mediaType = storageFile.contentType;
  656. var result = 'data:' + mediaType + ';base64,' + strBase64;
  657. win(result);
  658. }
  659. );
  660. }, function () {
  661. fail(FileError.NOT_FOUND_ERR);
  662. }
  663. );
  664. },
  665. getDirectory: function (win, fail, args) {
  666. var dirurl = args[0];
  667. var path = args[1];
  668. var options = args[2];
  669. var fs = getFilesystemFromURL(dirurl);
  670. var dirpath = pathFromURL(dirurl);
  671. if (!fs || !validName(path)) {
  672. fail(FileError.ENCODING_ERR);
  673. return;
  674. }
  675. var fspath = sanitize(dirpath + '/' + path);
  676. var completePath = sanitize(fs.winpath + fspath);
  677. var name = completePath.substring(completePath.lastIndexOf('/') + 1);
  678. var wpath = cordovaPathToNative(completePath.substring(0, completePath.lastIndexOf('/')));
  679. var flag = '';
  680. if (options) {
  681. flag = new Flags(options.create, options.exclusive);
  682. } else {
  683. flag = new Flags(false, false);
  684. }
  685. getFolderFromPathAsync(wpath).done(
  686. function (storageFolder) {
  687. if (flag.create === true && flag.exclusive === true) {
  688. storageFolder.createFolderAsync(name, Windows.Storage.CreationCollisionOption.failIfExists).done(
  689. function (storageFolder) {
  690. win(new DirectoryEntry(storageFolder.name, fspath, fs.name, fs.makeNativeURL(fspath)));
  691. }, function (err) { // eslint-disable-line handle-callback-err
  692. fail(FileError.PATH_EXISTS_ERR);
  693. }
  694. );
  695. } else if (flag.create === true && flag.exclusive === false) {
  696. storageFolder.createFolderAsync(name, Windows.Storage.CreationCollisionOption.openIfExists).done(
  697. function (storageFolder) {
  698. win(new DirectoryEntry(storageFolder.name, fspath, fs.name, fs.makeNativeURL(fspath)));
  699. }, function () {
  700. fail(FileError.INVALID_MODIFICATION_ERR);
  701. }
  702. );
  703. } else if (flag.create === false) {
  704. storageFolder.getFolderAsync(name).done(
  705. function (storageFolder) {
  706. win(new DirectoryEntry(storageFolder.name, fspath, fs.name, fs.makeNativeURL(fspath)));
  707. },
  708. function () {
  709. // check if path actually points to a file
  710. storageFolder.getFileAsync(name).done(
  711. function () {
  712. fail(FileError.TYPE_MISMATCH_ERR);
  713. }, function () {
  714. fail(FileError.NOT_FOUND_ERR);
  715. }
  716. );
  717. }
  718. );
  719. }
  720. }, function () {
  721. fail(FileError.NOT_FOUND_ERR);
  722. }
  723. );
  724. },
  725. remove: function (win, fail, args) {
  726. var fs = getFilesystemFromURL(args[0]);
  727. var path = pathFromURL(args[0]);
  728. if (!fs || !validName(path)) {
  729. fail(FileError.ENCODING_ERR);
  730. return;
  731. }
  732. // FileSystem root can't be removed!
  733. if (!path || path === '/') {
  734. fail(FileError.NO_MODIFICATION_ALLOWED_ERR);
  735. return;
  736. }
  737. var fullPath = cordovaPathToNative(fs.winpath + path);
  738. getFileFromPathAsync(fullPath).then(
  739. function (storageFile) {
  740. storageFile.deleteAsync().done(win, function () {
  741. fail(FileError.INVALID_MODIFICATION_ERR);
  742. });
  743. },
  744. function () {
  745. getFolderFromPathAsync(fullPath).done(
  746. function (sFolder) {
  747. sFolder.getFilesAsync()
  748. // check for files
  749. .then(function (fileList) {
  750. if (fileList) {
  751. if (fileList.length === 0) {
  752. return sFolder.getFoldersAsync();
  753. } else {
  754. fail(FileError.INVALID_MODIFICATION_ERR);
  755. }
  756. }
  757. })
  758. // check for folders
  759. .done(function (folderList) {
  760. if (folderList) {
  761. if (folderList.length === 0) {
  762. sFolder.deleteAsync().done(
  763. win,
  764. function () {
  765. fail(FileError.INVALID_MODIFICATION_ERR);
  766. }
  767. );
  768. } else {
  769. fail(FileError.INVALID_MODIFICATION_ERR);
  770. }
  771. }
  772. });
  773. },
  774. function () {
  775. fail(FileError.NOT_FOUND_ERR);
  776. }
  777. );
  778. }
  779. );
  780. },
  781. removeRecursively: function (successCallback, fail, args) {
  782. var fs = getFilesystemFromURL(args[0]);
  783. var path = pathFromURL(args[0]);
  784. if (!fs || !validName(path)) {
  785. fail(FileError.ENCODING_ERR);
  786. return;
  787. }
  788. // FileSystem root can't be removed!
  789. if (!path || path === '/') {
  790. fail(FileError.NO_MODIFICATION_ALLOWED_ERR);
  791. return;
  792. }
  793. var fullPath = cordovaPathToNative(fs.winpath + path);
  794. getFolderFromPathAsync(fullPath).done(function (storageFolder) {
  795. storageFolder.deleteAsync().done(function (res) {
  796. successCallback(res);
  797. }, function (err) {
  798. fail(err);
  799. });
  800. }, function () {
  801. fail(FileError.FILE_NOT_FOUND_ERR);
  802. });
  803. },
  804. getFile: function (win, fail, args) {
  805. var dirurl = args[0];
  806. var path = args[1];
  807. var options = args[2];
  808. var fs = getFilesystemFromURL(dirurl);
  809. var dirpath = pathFromURL(dirurl);
  810. if (!fs || !validName(path)) {
  811. fail(FileError.ENCODING_ERR);
  812. return;
  813. }
  814. var fspath = sanitize(dirpath + '/' + path);
  815. var completePath = sanitize(fs.winpath + fspath);
  816. var fileName = completePath.substring(completePath.lastIndexOf('/') + 1);
  817. var wpath = cordovaPathToNative(completePath.substring(0, completePath.lastIndexOf('/')));
  818. var flag = '';
  819. if (options !== null) {
  820. flag = new Flags(options.create, options.exclusive);
  821. } else {
  822. flag = new Flags(false, false);
  823. }
  824. getFolderFromPathAsync(wpath).done(
  825. function (storageFolder) {
  826. if (flag.create === true && flag.exclusive === true) {
  827. storageFolder.createFileAsync(fileName, Windows.Storage.CreationCollisionOption.failIfExists).done(
  828. function (storageFile) {
  829. win(new FileEntry(storageFile.name, fspath, fs.name, fs.makeNativeURL(fspath)));
  830. }, function () {
  831. fail(FileError.PATH_EXISTS_ERR);
  832. }
  833. );
  834. } else if (flag.create === true && flag.exclusive === false) {
  835. storageFolder.createFileAsync(fileName, Windows.Storage.CreationCollisionOption.openIfExists).done(
  836. function (storageFile) {
  837. win(new FileEntry(storageFile.name, fspath, fs.name, fs.makeNativeURL(fspath)));
  838. }, function () {
  839. fail(FileError.INVALID_MODIFICATION_ERR);
  840. }
  841. );
  842. } else if (flag.create === false) {
  843. storageFolder.getFileAsync(fileName).done(
  844. function (storageFile) {
  845. win(new FileEntry(storageFile.name, fspath, fs.name, fs.makeNativeURL(fspath)));
  846. }, function () {
  847. // check if path actually points to a folder
  848. storageFolder.getFolderAsync(fileName).done(
  849. function () {
  850. fail(FileError.TYPE_MISMATCH_ERR);
  851. }, function () {
  852. fail(FileError.NOT_FOUND_ERR);
  853. });
  854. }
  855. );
  856. }
  857. }, function (err) {
  858. fail(
  859. err.number === WinError.accessDenied ?
  860. FileError.SECURITY_ERR :
  861. FileError.NOT_FOUND_ERR
  862. );
  863. }
  864. );
  865. },
  866. readEntries: function (win, fail, args) { // ["fullPath"]
  867. var fs = getFilesystemFromURL(args[0]);
  868. var path = pathFromURL(args[0]);
  869. if (!fs || !validName(path)) {
  870. fail(FileError.ENCODING_ERR);
  871. return;
  872. }
  873. var fullPath = cordovaPathToNative(fs.winpath + path);
  874. var result = [];
  875. getFolderFromPathAsync(fullPath).done(function (storageFolder) {
  876. var promiseArr = [];
  877. var index = 0;
  878. promiseArr[index++] = storageFolder.getFilesAsync().then(function (fileList) {
  879. if (fileList !== null) {
  880. for (var i = 0; i < fileList.length; i++) {
  881. var fspath = getFsPathForWinPath(fs, fileList[i].path);
  882. if (!fspath) {
  883. fail(FileError.NOT_FOUND_ERR);
  884. return;
  885. }
  886. result.push(new FileEntry(fileList[i].name, fspath, fs.name, fs.makeNativeURL(fspath)));
  887. }
  888. }
  889. });
  890. promiseArr[index++] = storageFolder.getFoldersAsync().then(function (folderList) {
  891. if (folderList !== null) {
  892. for (var j = 0; j < folderList.length; j++) {
  893. var fspath = getFsPathForWinPath(fs, folderList[j].path);
  894. if (!fspath) {
  895. fail(FileError.NOT_FOUND_ERR);
  896. return;
  897. }
  898. result.push(new DirectoryEntry(folderList[j].name, fspath, fs.name, fs.makeNativeURL(fspath)));
  899. }
  900. }
  901. });
  902. WinJS.Promise.join(promiseArr).then(function () {
  903. win(result);
  904. });
  905. }, function () { fail(FileError.NOT_FOUND_ERR); });
  906. },
  907. write: function (win, fail, args) {
  908. var url = args[0];
  909. var data = args[1];
  910. var position = args[2];
  911. var isBinary = args[3];
  912. var fs = getFilesystemFromURL(url);
  913. var path = pathFromURL(url);
  914. if (!fs) {
  915. fail(FileError.ENCODING_ERR);
  916. return;
  917. }
  918. var completePath = sanitize(fs.winpath + path);
  919. var fileName = completePath.substring(completePath.lastIndexOf('/') + 1);
  920. var dirpath = completePath.substring(0, completePath.lastIndexOf('/'));
  921. var wpath = cordovaPathToNative(dirpath);
  922. function getWriteMethodForData (data, isBinary) {
  923. if (data instanceof Blob) { // eslint-disable-line no-undef
  924. return writeBlobAsync;
  925. }
  926. if (data instanceof ArrayBuffer) {
  927. return writeArrayBufferAsync;
  928. }
  929. if (isBinary) {
  930. return writeBytesAsync;
  931. }
  932. if (typeof data === 'string') {
  933. return writeTextAsync;
  934. }
  935. throw new Error('Unsupported data type for write method');
  936. }
  937. var writePromise = getWriteMethodForData(data, isBinary);
  938. getFolderFromPathAsync(wpath).done(
  939. function (storageFolder) {
  940. storageFolder.createFileAsync(fileName, Windows.Storage.CreationCollisionOption.openIfExists).done(
  941. function (storageFile) {
  942. writePromise(storageFile, data, position).done(
  943. function (bytesWritten) {
  944. var written = bytesWritten || data.length;
  945. win(written);
  946. },
  947. function () {
  948. fail(FileError.INVALID_MODIFICATION_ERR);
  949. }
  950. );
  951. },
  952. function () {
  953. fail(FileError.INVALID_MODIFICATION_ERR);
  954. }
  955. );
  956. },
  957. function () {
  958. fail(FileError.NOT_FOUND_ERR);
  959. }
  960. );
  961. },
  962. truncate: function (win, fail, args) { // ["fileName","size"]
  963. var url = args[0];
  964. var size = args[1];
  965. var fs = getFilesystemFromURL(url);
  966. var path = pathFromURL(url);
  967. if (!fs) {
  968. fail(FileError.ENCODING_ERR);
  969. return;
  970. }
  971. var completePath = sanitize(fs.winpath + path);
  972. var wpath = cordovaPathToNative(completePath);
  973. var dirwpath = cordovaPathToNative(completePath.substring(0, completePath.lastIndexOf('/')));
  974. getFileFromPathAsync(wpath).done(function (storageFile) {
  975. // the current length of the file.
  976. var leng = 0;
  977. storageFile.getBasicPropertiesAsync().then(function (basicProperties) {
  978. leng = basicProperties.size;
  979. if (Number(size) >= leng) {
  980. win(this.length);
  981. return;
  982. }
  983. if (Number(size) >= 0) {
  984. Windows.Storage.FileIO.readTextAsync(storageFile, Windows.Storage.Streams.UnicodeEncoding.utf8).then(function (fileContent) {
  985. fileContent = fileContent.substr(0, size);
  986. var name = storageFile.name;
  987. storageFile.deleteAsync().then(function () {
  988. return getFolderFromPathAsync(dirwpath);
  989. }).done(function (storageFolder) {
  990. storageFolder.createFileAsync(name).then(function (newStorageFile) {
  991. Windows.Storage.FileIO.writeTextAsync(newStorageFile, fileContent).done(function () {
  992. win(String(fileContent).length);
  993. }, function () {
  994. fail(FileError.NO_MODIFICATION_ALLOWED_ERR);
  995. });
  996. }, function () {
  997. fail(FileError.NO_MODIFICATION_ALLOWED_ERR);
  998. });
  999. });
  1000. }, function () { fail(FileError.NOT_FOUND_ERR); });
  1001. }
  1002. });
  1003. }, function () { fail(FileError.NOT_FOUND_ERR); });
  1004. },
  1005. copyTo: function (success, fail, args) { // ["fullPath","parent", "newName"]
  1006. transport(success, fail, args,
  1007. {
  1008. fileOp: function (file, folder, name, coll) {
  1009. return file.copyAsync(folder, name, coll);
  1010. },
  1011. folderOp: function (src, dst, name) {
  1012. return copyFolder(src, dst, name);
  1013. }}
  1014. );
  1015. },
  1016. moveTo: function (success, fail, args) {
  1017. transport(success, fail, args,
  1018. {
  1019. fileOp: function (file, folder, name, coll) {
  1020. return file.moveAsync(folder, name, coll);
  1021. },
  1022. folderOp: function (src, dst, name) {
  1023. return moveFolder(src, dst, name);
  1024. }}
  1025. );
  1026. },
  1027. tempFileSystem: null,
  1028. persistentFileSystem: null,
  1029. requestFileSystem: function (win, fail, args) {
  1030. var type = args[0];
  1031. var size = args[1];
  1032. var MAX_SIZE = 10000000000;
  1033. if (size > MAX_SIZE) {
  1034. fail(FileError.QUOTA_EXCEEDED_ERR);
  1035. return;
  1036. }
  1037. var fs;
  1038. switch (type) {
  1039. case LocalFileSystem.TEMPORARY:
  1040. fs = getFS('temporary');
  1041. break;
  1042. case LocalFileSystem.PERSISTENT:
  1043. fs = getFS('persistent');
  1044. break;
  1045. }
  1046. if (fs) { win(fs); } else { fail(FileError.NOT_FOUND_ERR); }
  1047. },
  1048. resolveLocalFileSystemURI: function (success, fail, args) {
  1049. var uri = args[0];
  1050. var path = pathFromURL(uri);
  1051. var fs = getFilesystemFromURL(uri);
  1052. if (!fs || !validName(path)) {
  1053. fail(FileError.ENCODING_ERR);
  1054. return;
  1055. }
  1056. if (path.indexOf(fs.winpath) === 0) { path = path.substr(fs.winpath.length); }
  1057. var abspath = cordovaPathToNative(fs.winpath + path);
  1058. getFileFromPathAsync(abspath).done(
  1059. function (storageFile) {
  1060. success(new FileEntry(storageFile.name, path, fs.name, fs.makeNativeURL(path)));
  1061. }, function () {
  1062. getFolderFromPathAsync(abspath).done(
  1063. function (storageFolder) {
  1064. success(new DirectoryEntry(storageFolder.name, path, fs.name, fs.makeNativeURL(path)));
  1065. }, function () {
  1066. fail(FileError.NOT_FOUND_ERR);
  1067. }
  1068. );
  1069. }
  1070. );
  1071. }
  1072. };
  1073. require('cordova/exec/proxy').add('File', module.exports);