Ei kuvausta

FileWriter.js 10KB

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