FileWriter.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  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. var exec = require('cordova/exec'),
  22. FileError = require('./FileError'),
  23. ProgressEvent = require('./ProgressEvent');
  24. /**
  25. * This class writes to the mobile device file system.
  26. *
  27. * For Android:
  28. * The root directory is the root of the file system.
  29. * To write to the SD card, the file name is "sdcard/my_file.txt"
  30. *
  31. * @constructor
  32. * @param file {File} File object containing file properties
  33. * @param append if true write to the end of the file, otherwise overwrite the file
  34. */
  35. var FileWriter = function(file) {
  36. this.fileName = "";
  37. this.length = 0;
  38. if (file) {
  39. this.localURL = file.localURL || file;
  40. this.length = file.size || 0;
  41. }
  42. // default is to write at the beginning of the file
  43. this.position = 0;
  44. this.readyState = 0; // EMPTY
  45. this.result = null;
  46. // Error
  47. this.error = null;
  48. // Event handlers
  49. this.onwritestart = null; // When writing starts
  50. this.onprogress = null; // While writing the file, and reporting partial file data
  51. this.onwrite = null; // When the write has successfully completed.
  52. this.onwriteend = null; // When the request has completed (either in success or failure).
  53. this.onabort = null; // When the write has been aborted. For instance, by invoking the abort() method.
  54. this.onerror = null; // When the write has failed (see errors).
  55. };
  56. // States
  57. FileWriter.INIT = 0;
  58. FileWriter.WRITING = 1;
  59. FileWriter.DONE = 2;
  60. /**
  61. * Abort writing file.
  62. */
  63. FileWriter.prototype.abort = function() {
  64. // check for invalid state
  65. if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) {
  66. throw new FileError(FileError.INVALID_STATE_ERR);
  67. }
  68. // set error
  69. this.error = new FileError(FileError.ABORT_ERR);
  70. this.readyState = FileWriter.DONE;
  71. // If abort callback
  72. if (typeof this.onabort === "function") {
  73. this.onabort(new ProgressEvent("abort", {"target":this}));
  74. }
  75. // If write end callback
  76. if (typeof this.onwriteend === "function") {
  77. this.onwriteend(new ProgressEvent("writeend", {"target":this}));
  78. }
  79. };
  80. /**
  81. * Writes data to the file
  82. *
  83. * @param data text or blob to be written
  84. * @param isPendingBlobReadResult {Boolean} true if the data is the pending blob read operation result
  85. */
  86. FileWriter.prototype.write = function(data, isPendingBlobReadResult) {
  87. var that=this;
  88. var supportsBinary = (typeof window.Blob !== 'undefined' && typeof window.ArrayBuffer !== 'undefined');
  89. var isProxySupportBlobNatively = (cordova.platformId === "windows8" || cordova.platformId === "windows");
  90. var isBinary;
  91. // Check to see if the incoming data is a blob
  92. if (data instanceof File || (!isProxySupportBlobNatively && supportsBinary && data instanceof Blob)) {
  93. var fileReader = new FileReader();
  94. fileReader.onload = function() {
  95. // Call this method again, with the arraybuffer as argument
  96. FileWriter.prototype.write.call(that, this.result, true /* isPendingBlobReadResult */);
  97. };
  98. fileReader.onerror = function () {
  99. // DONE state
  100. that.readyState = FileWriter.DONE;
  101. // Save error
  102. that.error = this.error;
  103. // If onerror callback
  104. if (typeof that.onerror === "function") {
  105. that.onerror(new ProgressEvent("error", {"target":that}));
  106. }
  107. // If onwriteend callback
  108. if (typeof that.onwriteend === "function") {
  109. that.onwriteend(new ProgressEvent("writeend", {"target":that}));
  110. }
  111. };
  112. // WRITING state
  113. this.readyState = FileWriter.WRITING;
  114. if (supportsBinary) {
  115. fileReader.readAsArrayBuffer(data);
  116. } else {
  117. fileReader.readAsText(data);
  118. }
  119. return;
  120. }
  121. // Mark data type for safer transport over the binary bridge
  122. isBinary = supportsBinary && (data instanceof ArrayBuffer);
  123. if (isBinary && cordova.platformId === "windowsphone") {
  124. // create a plain array, using the keys from the Uint8Array view so that we can serialize it
  125. data = Array.apply(null, new Uint8Array(data));
  126. }
  127. // Throw an exception if we are already writing a file
  128. if (this.readyState === FileWriter.WRITING && !isPendingBlobReadResult) {
  129. throw new FileError(FileError.INVALID_STATE_ERR);
  130. }
  131. // WRITING state
  132. this.readyState = FileWriter.WRITING;
  133. var me = this;
  134. // If onwritestart callback
  135. if (typeof me.onwritestart === "function") {
  136. me.onwritestart(new ProgressEvent("writestart", {"target":me}));
  137. }
  138. // Write file
  139. exec(
  140. // Success callback
  141. function(r) {
  142. // If DONE (cancelled), then don't do anything
  143. if (me.readyState === FileWriter.DONE) {
  144. return;
  145. }
  146. // position always increases by bytes written because file would be extended
  147. me.position += r;
  148. // The length of the file is now where we are done writing.
  149. me.length = me.position;
  150. // DONE state
  151. me.readyState = FileWriter.DONE;
  152. // If onwrite callback
  153. if (typeof me.onwrite === "function") {
  154. me.onwrite(new ProgressEvent("write", {"target":me}));
  155. }
  156. // If onwriteend callback
  157. if (typeof me.onwriteend === "function") {
  158. me.onwriteend(new ProgressEvent("writeend", {"target":me}));
  159. }
  160. },
  161. // Error callback
  162. function(e) {
  163. // If DONE (cancelled), then don't do anything
  164. if (me.readyState === FileWriter.DONE) {
  165. return;
  166. }
  167. // DONE state
  168. me.readyState = FileWriter.DONE;
  169. // Save error
  170. me.error = new FileError(e);
  171. // If onerror callback
  172. if (typeof me.onerror === "function") {
  173. me.onerror(new ProgressEvent("error", {"target":me}));
  174. }
  175. // If onwriteend callback
  176. if (typeof me.onwriteend === "function") {
  177. me.onwriteend(new ProgressEvent("writeend", {"target":me}));
  178. }
  179. }, "File", "write", [this.localURL, data, this.position, isBinary]);
  180. };
  181. /**
  182. * Moves the file pointer to the location specified.
  183. *
  184. * If the offset is a negative number the position of the file
  185. * pointer is rewound. If the offset is greater than the file
  186. * size the position is set to the end of the file.
  187. *
  188. * @param offset is the location to move the file pointer to.
  189. */
  190. FileWriter.prototype.seek = function(offset) {
  191. // Throw an exception if we are already writing a file
  192. if (this.readyState === FileWriter.WRITING) {
  193. throw new FileError(FileError.INVALID_STATE_ERR);
  194. }
  195. if (!offset && offset !== 0) {
  196. return;
  197. }
  198. // See back from end of file.
  199. if (offset < 0) {
  200. this.position = Math.max(offset + this.length, 0);
  201. }
  202. // Offset is bigger than file size so set position
  203. // to the end of the file.
  204. else if (offset > this.length) {
  205. this.position = this.length;
  206. }
  207. // Offset is between 0 and file size so set the position
  208. // to start writing.
  209. else {
  210. this.position = offset;
  211. }
  212. };
  213. /**
  214. * Truncates the file to the size specified.
  215. *
  216. * @param size to chop the file at.
  217. */
  218. FileWriter.prototype.truncate = function(size) {
  219. // Throw an exception if we are already writing a file
  220. if (this.readyState === FileWriter.WRITING) {
  221. throw new FileError(FileError.INVALID_STATE_ERR);
  222. }
  223. // WRITING state
  224. this.readyState = FileWriter.WRITING;
  225. var me = this;
  226. // If onwritestart callback
  227. if (typeof me.onwritestart === "function") {
  228. me.onwritestart(new ProgressEvent("writestart", {"target":this}));
  229. }
  230. // Write file
  231. exec(
  232. // Success callback
  233. function(r) {
  234. // If DONE (cancelled), then don't do anything
  235. if (me.readyState === FileWriter.DONE) {
  236. return;
  237. }
  238. // DONE state
  239. me.readyState = FileWriter.DONE;
  240. // Update the length of the file
  241. me.length = r;
  242. me.position = Math.min(me.position, r);
  243. // If onwrite callback
  244. if (typeof me.onwrite === "function") {
  245. me.onwrite(new ProgressEvent("write", {"target":me}));
  246. }
  247. // If onwriteend callback
  248. if (typeof me.onwriteend === "function") {
  249. me.onwriteend(new ProgressEvent("writeend", {"target":me}));
  250. }
  251. },
  252. // Error callback
  253. function(e) {
  254. // If DONE (cancelled), then don't do anything
  255. if (me.readyState === FileWriter.DONE) {
  256. return;
  257. }
  258. // DONE state
  259. me.readyState = FileWriter.DONE;
  260. // Save error
  261. me.error = new FileError(e);
  262. // If onerror callback
  263. if (typeof me.onerror === "function") {
  264. me.onerror(new ProgressEvent("error", {"target":me}));
  265. }
  266. // If onwriteend callback
  267. if (typeof me.onwriteend === "function") {
  268. me.onwriteend(new ProgressEvent("writeend", {"target":me}));
  269. }
  270. }, "File", "truncate", [this.localURL, size]);
  271. };
  272. module.exports = FileWriter;