├── .pr-preview.json ├── w3c.json ├── LICENSE.md ├── CODE_OF_CONDUCT.md ├── README.md ├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── auto-publish.yml ├── section-links.js ├── CONTRIBUTING.md ├── dfn.js ├── FileAPI.css └── index.bs /.pr-preview.json: -------------------------------------------------------------------------------- 1 | { 2 | "src_file": "index.bs", 3 | "type": "bikeshed", 4 | "params": { 5 | "force": 1 6 | } 7 | } -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": [ 3 | "114929" 4 | ], 5 | "contacts": [ 6 | "siusin" 7 | ], 8 | "shortName": "FileAPI", 9 | "repo-type": "rec-track" 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All documents in this Repository are licensed by contributors 2 | under the 3 | [W3C Software and Document License](http://www.w3.org/Consortium/Legal/copyright-software). 4 | 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | All documentation, code and communication under this repository are covered by the [W3C Code of Ethics and Professional Conduct](https://www.w3.org/Consortium/cepc/). 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | File API Specification 2 | ---------------------- 3 | 4 | This repository is for editing drafts and discussion of the 5 | [File API](https://w3c.github.io/FileAPI/) specification. 6 | 7 | This spec was started with W3C's CVS repository but usage of 8 | [that repository](http://dev.w3.org/cvsweb/2006/webapi/FileAPI/#dirlist) has stopped. 9 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Closes #??? 2 | 3 | For *normative* changes, the following tasks have been completed: 4 | 5 | * [ ] Modified Web platform tests (link to pull request) 6 | 7 | Implementation commitment: 8 | 9 | * [ ] WebKit (https://bugs.webkit.org/show_bug.cgi?id=) 10 | * [ ] Chromium (https://bugs.chromium.org/p/chromium/issues/detail?id=) 11 | * [ ] Gecko (https://bugzilla.mozilla.org/show_bug.cgi?id=) 12 | -------------------------------------------------------------------------------- /.github/workflows/auto-publish.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: {} 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | main: 9 | name: Build, Validate and Deploy 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: w3c/spec-prod@v2 14 | with: 15 | GH_PAGES_BRANCH: gh-pages 16 | VALIDATE_MARKUP: false 17 | W3C_ECHIDNA_TOKEN: ${{ secrets.ECHIDNA_TOKEN }} 18 | W3C_WG_DECISION_URL: https://lists.w3.org/Archives/Public/public-webappsec/2015Mar/0170.html 19 | W3C_BUILD_OVERRIDE: | 20 | shortname: FileAPI 21 | status: WD 22 | -------------------------------------------------------------------------------- /section-links.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function(event) { 2 | function f(n) { 3 | if (n.nodeType == 1 && n.tagName.match(/^H[1-6]$/)) { 4 | var span = document.createElement('span'); 5 | span.className = 'section-link'; 6 | span.textContent = '\xa0'; 7 | var a = document.createElement('a'); 8 | a.href = '#' + n.parentNode.id; 9 | a.textContent = '\xb6'; 10 | span.appendChild(a); 11 | n.appendChild(span); 12 | } else { 13 | n = n.firstChild; 14 | while (n) { 15 | f(n); 16 | n = n.nextSibling; 17 | } 18 | } 19 | } 20 | f(document.getElementById('sections')); 21 | }, false); 22 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Web Applications Working Group 2 | 3 | Contributions to this repository are intended to become part of Recommendation-track documents governed by the 4 | [W3C Patent Policy](http://www.w3.org/Consortium/Patent-Policy/) and 5 | [Software and Document License](http://www.w3.org/Consortium/Legal/copyright-software). To make substantive contributions to specifications, you must either participate 6 | in the relevant W3C Working Group or make a non-member patent licensing commitment. 7 | 8 | # Pull Requests 9 | 10 | To make a pull request, please edit the `index.bs` in the `master` branch, our travis bot will generate an `index.html` and push the file to `gh-pages` branch once you submit your patch. 11 | 12 | If you are not the sole contributor to a contribution (pull request), please identify all 13 | contributors in the pull request comment. 14 | 15 | To add a contributor (other than yourself, that's automatic), mark them one per line as follows: 16 | 17 | ``` 18 | +@github_username 19 | ``` 20 | 21 | If you added a contributor by mistake, you can remove them in a comment with: 22 | 23 | ``` 24 | -@github_username 25 | ``` 26 | 27 | If you are making a pull request on behalf of someone else but you had no part in designing the 28 | feature, you can remove yourself with the above syntax. 29 | 30 | # Tests 31 | 32 | For normative changes, a corresponding 33 | [web-platform-tests](https://github.com/web-platform-tests/wpt) PR is highly appreciated. Typically, 34 | both PRs will be merged at the same time. Note that a test change that contradicts the spec should 35 | not be merged before the corresponding spec change. If testing is not practical, please explain why 36 | and if appropriate [file an issue](https://github.com/web-platform-tests/wpt/issues/new) to follow 37 | up later. Add the `type:untestable` or `type:missing-coverage` label as appropriate. 38 | -------------------------------------------------------------------------------- /dfn.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Taken from http://www.whatwg.org/specs/web-apps/current-work/dfn.js 3 | * as of Wed Dec 10 13:08:00 Australia/Melbourne 2008. 4 | * 5 | * With modifications to make it work with the Web IDL section structure. 6 | */ 7 | 8 | // dfn.js 9 | // makes elements link back to all uses of the term 10 | // no copyright is asserted on this file 11 | 12 | var dfnMapTarget = -1; 13 | var dfnMapDone = 0; 14 | var dfnMap = {}; 15 | document.addEventListener('DOMContentLoaded', function (event) { 16 | var links = []; 17 | dfnMapTarget = document.links.length; 18 | for (var i = 0; i < dfnMapTarget; i += 1) 19 | links[i] = document.links[i]; 20 | var inc = 100; 21 | for (var i = 0; i < dfnMapTarget; i += inc) { 22 | setTimeout(function (j) { 23 | for (var k = j; k < j+inc && k < dfnMapTarget; k += 1) { 24 | if (links[k].href.indexOf('#') >= 0) { 25 | if (links[k].className != "no-backref" && 26 | links[k].parentNode.className != "no-backref") { 27 | var s = links[k].href.substr(links[k].href.indexOf('#') + 1); 28 | if (!(s in dfnMap)) 29 | dfnMap[s] = []; 30 | dfnMap[s].push(links[k]); 31 | } 32 | } 33 | dfnMapDone += 1; 34 | } 35 | }, 0, i); 36 | } 37 | document.body.className += " dfnEnabled"; 38 | }, false); 39 | 40 | var dfnPanel; 41 | var dfnUniqueId = 0; 42 | var dfnTimeout; 43 | document.addEventListener('click', dfnShow, false); 44 | function dfnShow(event) { 45 | if (dfnTimeout) { 46 | clearTimeout(dfnTimeout); 47 | dfnTimeout = null; 48 | } 49 | if (dfnPanel) { 50 | dfnPanel.parentNode.removeChild(dfnPanel); 51 | dfnPanel = null; 52 | } 53 | if (dfnMapDone == dfnMapTarget) { 54 | var node = event.target; 55 | while (node && (node.nodeType != event.target.ELEMENT_NODE || node.tagName != "DFN")) 56 | node = node.parentNode; 57 | if (node) { 58 | var panel = document.createElement('div'); 59 | panel.className = 'dfnPanel'; 60 | if (node.id) { 61 | var permalinkP = document.createElement('p'); 62 | var permalinkA = document.createElement('a'); 63 | permalinkA.href = '#' + node.id; 64 | permalinkA.textContent = '#' + node.id; 65 | permalinkP.appendChild(permalinkA); 66 | panel.appendChild(permalinkP); 67 | } 68 | var p = document.createElement('p'); 69 | panel.appendChild(p); 70 | if (node.id in dfnMap || node.parentNode.id in dfnMap) { 71 | p.textContent = 'Referenced in:'; 72 | var ul = document.createElement('ul'); 73 | var lastHeader; 74 | var lastLi; 75 | var n; 76 | var sourceLinks = []; 77 | if (node.id in dfnMap) 78 | for (var i = 0; i < dfnMap[node.id].length; i += 1) 79 | sourceLinks.push(dfnMap[node.id][i]); 80 | if (node.parentNode.id in dfnMap) 81 | for (var i = 0; i < dfnMap[node.parentNode.id].length; i += 1) 82 | sourceLinks.push(dfnMap[node.parentNode.id][i]); 83 | for (var i = 0; i < sourceLinks.length; i += 1) { 84 | var link = sourceLinks[i]; 85 | var header = dfnGetCaption(link); 86 | var a = document.createElement('a'); 87 | if (!link.id) 88 | link.id = 'dfnReturnLink-' + dfnUniqueId++; 89 | a.href = '#' + link.id; 90 | if (header != lastHeader) { 91 | lastHeader = header; 92 | n = 1; 93 | var li = document.createElement('li'); 94 | var cloneHeader = header.cloneNode(true); 95 | while (cloneHeader.hasChildNodes()) 96 | if (cloneHeader.firstChild.className == 'section-link') 97 | cloneHeader.removeChild(cloneHeader.firstChild); 98 | else 99 | a.appendChild(cloneHeader.firstChild); 100 | lastLi = li; 101 | li.appendChild(a); 102 | ul.appendChild(li); 103 | } else { 104 | n += 1; 105 | a.appendChild(document.createTextNode('(' + n + ')')); 106 | lastLi.appendChild(document.createTextNode(' ')); 107 | lastLi.appendChild(a); 108 | } 109 | } 110 | panel.appendChild(ul); 111 | } else { 112 | p.textContent = 'No references in this file.'; 113 | } 114 | node.appendChild(panel); 115 | dfnPanel = panel; 116 | } 117 | } else { 118 | dfnTimeout = setTimeout(dfnShow, 250, event); 119 | } 120 | } 121 | 122 | function dfnGetCaption(link) { 123 | var node = link; 124 | while (node && !(node.parentNode.tagName == "DIV" && node.parentNode.className == "section")) 125 | node = node.parentNode; 126 | while (node && (node.nodeType != node.ELEMENT_NODE || !node.tagName.match(/^H[1-6]$/))) 127 | node = node.previousSibling; 128 | return node; 129 | } 130 | -------------------------------------------------------------------------------- /FileAPI.css: -------------------------------------------------------------------------------- 1 | .nt, pre, .terminal, code, .prop, .esstring, .javavalue, .idlident, .idlstring, .xattr, .regex, .prod-number, .prod-lines, .prod-mid { 2 | font-size: 14px; 3 | } 4 | pre code, .prod-lines .nt { 5 | font-size: 14px !important; 6 | } 7 | .terminal, code, .prop, .esstring, .javavalue, .idlident, .idlstring, .example, .note, blockquote { 8 | background: #d9e8ff; 9 | } 10 | td code { 11 | background: inherit; 12 | } 13 | .example blockquote { 14 | background: #f0f6ff; 15 | } 16 | table.grammar { 17 | background: #eee; 18 | } 19 | .ednote { 20 | border-top: 3px solid red; 21 | border-bottom: 3px solid red; 22 | margin: 1em 2em; 23 | padding: 0 1em 0 1em; 24 | background: #f8eeee; 25 | } 26 | .ednoteHeader { 27 | font-weight: bold; 28 | display: block; 29 | padding-top: 0.5em; 30 | } 31 | .toc ul li { 32 | list-style-type: none; 33 | margin-top: 0; 34 | margin-bottom: 0; 35 | } 36 | .toc ul { 37 | margin-bottom: 0.5em; 38 | } 39 | .terminal, code, .prop, .esstring, .javavalue, .idlident, .idlstring, .input { 40 | font-family: /*Consolas, Monaco,*/ monospace !important; 41 | } 42 | pre.code code { 43 | background: inherit; 44 | } 45 | .propattrset { 46 | } 47 | /*.prop { 48 | font-family: Consolas, Monaco, monospace; 49 | }*/ 50 | 51 | .xattr { 52 | font-family: /*Consolas, Monaco,*/ monospace; 53 | } 54 | 55 | table { border-collapse:collapse; border-style:hidden hidden none hidden } 56 | table thead { border-bottom:solid } 57 | table tbody th:first-child { border-left:solid } 58 | table td, table th { border-left:solid; border-right:solid; border-bottom:solid thin; vertical-align:top; padding:0.2em } 59 | 60 | .nt, .prod-lines { 61 | font-family: /*Consolas, Monaco,*/ monospace; 62 | white-space: nowrap; 63 | } 64 | .idltype, .idlvalue { 65 | font-weight: bold; 66 | } 67 | .idlop { 68 | font-weight: bold; 69 | } 70 | .esvalue, .estype { 71 | font-weight: bold; 72 | } 73 | .javatype, .javapkg { 74 | font-weight: bold; 75 | } 76 | .regex { 77 | font-family: /*Consolas, Monaco,*/ monospace; 78 | white-space: nowrap; 79 | } 80 | .typevar { 81 | font-style: italic; 82 | } 83 | .example, .note { 84 | border-top: 3px solid #005a9c; 85 | border-bottom: 3px solid #005a9c; 86 | margin: 1em 2em; 87 | padding: 0 1em 0 1em; 88 | } 89 | .exampleHeader, .noteHeader { 90 | font-weight: bold; 91 | display: block; 92 | color: #005a9c; 93 | color: black; 94 | padding-top: 0.5em; 95 | } 96 | pre { 97 | overflow: auto; 98 | margin: 0; 99 | font-family: /*Consolas, Monaco,*/ monospace; 100 | } 101 | pre.code { 102 | padding: 0 1em; 103 | margin: 0; 104 | margin-bottom: 1em; 105 | } 106 | .block { 107 | border: 1px solid #90b8de; 108 | border-left: 3px double #90b8de; 109 | border-left: none; 110 | border-right: none; 111 | background: #f0f6ff; 112 | margin: 2em; 113 | margin-top: 1em; 114 | margin-bottom: 1em; 115 | padding: 0 0.5em; 116 | padding-bottom: 0.5em; 117 | } 118 | .blockTitleDiv { 119 | text-align: left; 120 | } 121 | .blockTitle { 122 | position: relative; 123 | top: -0.75em; 124 | left: -1.5em; 125 | /*border: 1px solid #90b8de; 126 | border-left: none; 127 | border-right: none;*/ 128 | background: #90b8de; 129 | color: white; 130 | padding: 0.25em 1em 0.25em 1em; 131 | font-weight: bold; 132 | font-size: 80%; 133 | } 134 | dfn { 135 | font-weight: bold; 136 | font-style: italic; 137 | } 138 | .dfnref { 139 | } 140 | li { 141 | margin-top: 0.5em; 142 | margin-bottom: 0.5em; 143 | } 144 | ul > li { 145 | list-style-type: disc; 146 | } 147 | .norm { 148 | font-style: italic; 149 | } 150 | .rfc2119 { 151 | text-transform: lowercase; 152 | font-variant: small-caps; 153 | } 154 | dfn var { 155 | font-style: normal; 156 | } 157 | blockquote { 158 | padding: 1px 1em; 159 | margin-left: 2em; 160 | margin-right: 2em; 161 | } 162 | a.placeholder { 163 | color: #00e; 164 | } 165 | dl.changes > dd { 166 | margin-left: 0; 167 | } 168 | dd > :first-child { 169 | margin-top: 0; 170 | } 171 | caption { 172 | caption-side: bottom; 173 | margin-top: 1em; 174 | font-weight: bold; 175 | } 176 | body { 177 | line-height: 1.3; 178 | } 179 | @media print { 180 | .section-link { 181 | display: none; 182 | } 183 | } 184 | .section-link { 185 | visibility: hidden; 186 | width: 1px; 187 | height: 1px; 188 | overflow: visible; 189 | font-size: 10pt; 190 | font-style: normal; 191 | } 192 | .section-link a { 193 | color: #666; 194 | font-weight: bold; 195 | text-decoration: none; 196 | } 197 | .section-link a:hover { 198 | color: #c00; 199 | } 200 | .section > *:hover > .section-link { 201 | visibility: visible; 202 | } 203 | div.set { 204 | margin-left: 3em; 205 | text-indent: -1em; 206 | } 207 | ol.algorithm ol { 208 | border-left: 1px solid #90b8de; 209 | margin-left: 1em; 210 | } 211 | dl.switch > dd > ol.only { 212 | margin-left: 0; 213 | } 214 | dl.switch { 215 | padding-left: 2em; 216 | } 217 | dl.switch > dt { 218 | text-indent: -1.5em; 219 | margin-top: 1em; 220 | } 221 | dl.switch > dt + dt { 222 | margin-top: 0; 223 | } 224 | dl.switch > dt:before { 225 | content: '\21AA'; 226 | padding: 0 0.5em 0 0; 227 | display: inline-block; 228 | width: 1em; 229 | text-align: right; 230 | line-height: 0.5em; 231 | } 232 | .diagram { 233 | text-align: center; 234 | } 235 | iframe { 236 | border: 0; 237 | } 238 | .ignore { 239 | opacity: 0.5; 240 | } 241 | .comment { 242 | color: #005a9c; 243 | } 244 | 245 | .matrix { 246 | border-collapse: collapse; 247 | margin-left: auto; 248 | margin-right: auto; 249 | } 250 | .matrix th { 251 | background: #d9e8ff; 252 | text-align: right; 253 | } 254 | .matrix td, .matrix th { 255 | border: 1px solid #90b8de; 256 | padding: 4px; 257 | } 258 | .matrix th.corner { 259 | border: 0; 260 | background: none; 261 | } 262 | .matrix td { 263 | text-align: center; 264 | background: #f0f6ff; 265 | } 266 | .matrix .belowdiagonal { 267 | background: #ddd; 268 | } 269 | 270 | ul.notes { font-size: 90%; padding-left: 0 } 271 | ul.notes li { list-style-type: none } 272 | ul.notes .note-link { vertical-align: super } 273 | .note-link { font-size: 90% } 274 | 275 | .code var { color: #f44; } 276 | 277 | /* For dfn.js */ 278 | body.dfnEnabled dfn { cursor: pointer; } 279 | .dfnPanel { 280 | display: inline; 281 | position: absolute; 282 | height: auto; 283 | width: auto; 284 | padding: 0.5em 0.75em; 285 | font: small sans-serif; 286 | background: #DDDDDD; 287 | color: black; 288 | border: outset 0.2em; 289 | cursor: default; 290 | } 291 | .dfnPanel * { margin: 0; padding: 0; font: inherit; text-indent: 0; } 292 | .dfnPanel :link, .dfnPanel :visited { color: black; } 293 | .dfnPanel p { font-weight: bolder; } 294 | .dfnPanel li { list-style-position: inside; } 295 | -------------------------------------------------------------------------------- /index.bs: -------------------------------------------------------------------------------- 1 |
   2 | Indent: 2
   3 | Title: File API
   4 | Shortname: FileAPI
   5 | Level: none
   6 | Group: webapps
   7 | Editor: Marijn Kruisselbrink, Google, mek@chromium.org, w3cid 72440
   8 | Former Editor: Arun Ranganathan, Mozilla Corporation, http://arunranga.com/, arun@mozilla.com, w3cid 37240
   9 | Prepare For TR: true
  10 | Status: ED
  11 | ED: https://w3c.github.io/FileAPI/
  12 | TR: https://www.w3.org/TR/FileAPI/
  13 | Repository: w3c/FileAPI
  14 | !Tests: web-platform-tests FileAPI/ (ongoing work)
  15 | Abstract: This specification provides an API for representing file objects in web applications,
  16 |   as well as programmatically selecting them and accessing their data. This includes:
  17 | 
  18 |   * A {{FileList}} interface, which represents an array of individually selected files from the underlying system. The user interface for selection can be invoked via <input type="file">, i.e. when the input element is in the File Upload state [[HTML]].
  19 |   * A {{Blob}} interface, which represents immutable raw binary data, and allows access to ranges of bytes within the {{Blob}} object as a separate {{Blob}}.
  20 |   * A {{File}} interface, which includes readonly informational attributes about a file such as its name and the date of the last modification (on disk) of the file.
  21 |   * A {{FileReader}} interface, which provides methods to read a {{File}} or a {{Blob}}, and an event model to obtain the results of these reads.
  22 |   * A URL scheme for use with binary data such as files, so that they can be referenced within web applications.
  23 | 
  24 |   Additionally, this specification defines objects to be used within threaded web applications for the synchronous reading of files.
  25 | 
  26 |   [[#requirements]] covers the motivation behind this specification.
  27 | 
  28 |   This API is designed to be used in conjunction with other APIs and elements on the web platform, notably:
  29 |   {{XMLHttpRequest}} (e.g. with an overloaded {{XMLHttpRequest/send()}} method for {{File}} or {{Blob}} arguments),
  30 |   {{Worker/postMessage(message, options)|postMessage()}},
  31 |   {{DataTransfer}} (part of the drag and drop API defined in [[HTML]])
  32 |   and Web Workers.
  33 |   Additionally, it should be possible to programmatically obtain a list of files from the <{input}> element
  34 |   when it is in the File Upload state [[HTML]].
  35 |   These kinds of behaviors are defined in the appropriate affiliated specifications.
  36 | Status Text: Previous discussion of this specification has taken place on two other mailing lists: public-webapps@w3.org (archive) and public-webapi@w3.org (archive). Ongoing discussion will be on the public-webapps@w3.org mailing list.
  37 | 
  38 |   This draft consists of changes made to the previous Last Call Working Draft. Please send comments to the public-webapi@w3.org as described above. You can see Last Call Feedback on the W3C Wiki: http://www.w3.org/wiki/Webapps/LCWD-FileAPI-20130912
  39 | 
  40 |   An [implementation report](https://wpt.fyi/results/FileAPI) is automatically generated from the test suite.
  41 | Translate Ids: dictdef-blobpropertybag dfn-BlobPropertyBag, dictdef-filepropertybag dfn-FilePropertyBag, filereadersync dfn-FileReaderSync, filelist dfn-filelist, filereader dfn-filereader, file dfn-file, blob dfn-Blob, blob-section blob, file-section file
  42 | Markup Shorthands: css no, markdown yes
  43 | 
44 | 45 | 69 | 70 |
  71 | spec: mimesniff; urlPrefix: https://mimesniff.spec.whatwg.org/
  72 |   type: dfn
  73 |     text: parsable mime type
  74 | spec: ecma-262; urlPrefix: http://tc39.github.io/ecma262/
  75 |   type: interface
  76 |     text: Array; url: sec-array-constructor
  77 |     text: Date; url: sec-date-constructor
  78 | spec: media-source; urlPrefix: http://w3c.github.io/media-source/
  79 |   type: interface
  80 |     text: MediaSource; url: #mediasource
  81 | 
82 | 83 | # Introduction # {#intro} 84 | 85 | *This section is informative.* 86 | 87 | Web applications should have the ability to manipulate as wide as possible a range of user input, 88 | including files that a user may wish to upload to a remote server or manipulate inside a rich web application. 89 | This specification defines the basic representations for files, 90 | lists of files, 91 | errors raised by access to files, 92 | and programmatic ways to read files. 93 | Additionally, this specification also defines an interface that represents "raw data" 94 | which can be asynchronously processed on the main thread of conforming user agents. 95 | The interfaces and API defined in this specification can be used with other interfaces and APIs exposed to the web platform. 96 | 97 | The {{File}} interface represents file data typically obtained from the underlying file system, 98 | and the {{Blob}} interface 99 | ("Binary Large Object" - a name originally introduced to web APIs in Google Gears) 100 | represents immutable raw data. 101 | {{File}} or {{Blob}} reads should happen asynchronously on the main thread, 102 | with an optional synchronous API used within threaded web applications. 103 | An asynchronous API for reading files prevents blocking and UI "freezing" on a user agent's main thread. 104 | This specification defines an asynchronous API based on an *event model* 105 | to read and access a {{File}} or {{Blob}}’s data. 106 | A {{FileReader}} object provides asynchronous read methods to access that file's data 107 | through event handler content attributes and the firing of events. 108 | The use of events and event handlers allows separate code blocks the ability 109 | to monitor the *progress of the read* 110 | (which is particularly useful for remote drives or mounted drives, 111 | where file access performance may vary from local drives) 112 | and error conditions that may arise during reading of a file. 113 | An example will be illustrative. 114 | 115 |
116 | In the example below, different code blocks handle progress, error, and success conditions. 117 | 118 |
 119 |   function startRead() {
 120 |     // obtain input element through DOM
 121 | 
 122 |     var file = document.getElementById('file').files[0];
 123 |     if(file){
 124 |       getAsText(file);
 125 |     }
 126 |   }
 127 | 
 128 |   function getAsText(readFile) {
 129 | 
 130 |     var reader = new FileReader();
 131 | 
 132 |     // Read file into memory as UTF-16
 133 |     reader.readAsText(readFile, "UTF-16");
 134 | 
 135 |     // Handle progress, success, and errors
 136 |     reader.onprogress = updateProgress;
 137 |     reader.onload = loaded;
 138 |     reader.onerror = errorHandler;
 139 |   }
 140 | 
 141 |   function updateProgress(evt) {
 142 |     if (evt.lengthComputable) {
 143 |       // evt.loaded and evt.total are ProgressEvent properties
 144 |       var loaded = (evt.loaded / evt.total);
 145 |       if (loaded < 1) {
 146 |         // Increase the prog bar length
 147 |         // style.width = (loaded * 200) + "px";
 148 |       }
 149 |     }
 150 |   }
 151 | 
 152 |   function loaded(evt) {
 153 |     // Obtain the read file data
 154 |     var fileString = evt.target.result;
 155 |     // Handle UTF-16 file dump
 156 |     if(utils.regexp.isChinese(fileString)) {
 157 |       //Chinese Characters + Name validation
 158 |     }
 159 |     else {
 160 |       // run other charset test
 161 |     }
 162 |     // xhr.send(fileString)
 163 |   }
 164 | 
 165 |   function errorHandler(evt) {
 166 |     if(evt.target.error.name == "NotReadableError") {
 167 |       // The file could not be read
 168 |     }
 169 |   }
 170 |   
171 |
172 | 173 | # Terminology and Algorithms # {#terminology} 174 | 175 | When this specification says to terminate an algorithm 176 | the user agent must terminate the algorithm after finishing the step it is on. 177 | Asynchronous read methods defined in this specification may return before the algorithm in question is terminated, 178 | and can be terminated by an {{FileReader/abort()}} call. 179 | 180 | The algorithms and steps in this specification use the following mathematical operations: 181 | 182 | * max(a,b) returns the maximum of a and b, 183 | and is always performed on integers as they are defined in WebIDL [[WebIDL]]; 184 | in the case of max(6,4) the result is 6. 185 | This operation is also defined in ECMAScript [[!ECMA-262]]. 186 | * min(a,b) returns the minimum of a and b, 187 | and is always performed on integers as they are defined in WebIDL [[WebIDL]]; 188 | in the case of min(6,4) the result is 4. 189 | This operation is also defined in ECMAScript [[ECMA-262]]. 190 | * Mathematical comparisons such as < (less than), ≤ (less than or equal to), and > (greater than) are as in ECMAScript [[ECMA-262]]. 191 | 192 | The term Unix Epoch is used in this specification to refer to the time 00:00:00 UTC on January 1 1970 193 | (or 1970-01-01T00:00:00Z ISO 8601); 194 | this is the same time that is conceptually "0" in ECMA-262 [[ECMA-262]]. 195 | 196 |
197 | The slice blob algorithm given a {{Blob}} blob, start, 198 | end, and contentType is used to refer to the following 199 | steps and returns a new {{Blob}} containing the bytes ranging from the start parameter 200 | up to but not including the end parameter. It must act as follows: 201 | 202 | 1. Let originalSize be blob's {{Blob/size}}. 203 | 204 | 1. The start parameter, if non-null, is a value for the start point of a slice blob 205 | call, and must be treated as a byte-order position, with the zeroth position representing the 206 | first byte. User agents must normalize start according to the following: 207 | 208 |
    209 |
  1. If start is null, let relativeStart be 0. 210 | 211 |
  2. If start is negative, let relativeStart be 212 | max((originalSize + start), 0). 213 | 214 |
  3. Otherwise, let relativeStart be 215 | min(start, originalSize). 216 |
217 | 218 | 1. The end parameter, if non-null. is a value for the end point of a slice blob 219 | call. User agents must normalize end according to the following: 220 | 221 |
    222 |
  1. If end is null, let relativeEnd be originalSize. 223 | 224 |
  2. If end is negative, let relativeEnd be 225 | max((originalSize + end), 0). 226 | 227 |
  3. Otherwise, let relativeEnd be 228 | min(end, originalSize). 229 |
230 | 231 | 1. The contentType parameter, if non-null, is used to set the ASCII-encoded string in 232 | lower case representing the media type of the {{Blob}}. User agents must normalize 233 | contentType according to the following: 234 | 235 |
    236 |
  1. If contentType is null, let relativeContentType be set to the empty 237 | string. 238 | 239 |
  2. Otherwise, let relativeContentType be set to contentType and run the 240 | substeps below: 241 | 1. If relativeContentType contains any characters outside the range of U+0020 to 242 | U+007E, then set relativeContentType to the empty string and return from these 243 | substeps. 244 | 2. Convert every character in relativeContentType to [=ASCII lowercase=]. 245 |
246 | 247 | 1. Let span be max((relativeEnd - relativeStart), 0). 248 | 249 | 1. Return a new {{Blob}} object S with the following characteristics: 250 | 251 |
    252 |
  1. S refers to span consecutive bytes from blob's 253 | associated byte sequence, beginning with the byte at byte-order position 254 | relativeStart. 255 | 256 |
  2. S.{{Blob/size}} = span. 257 | 258 |
  3. S.{{Blob/type}} = relativeContentType. 259 |
260 | 261 |
262 | 263 | 272 | 273 | # The Blob Interface and Binary Data # {#blob-section} 274 | 275 | A {{Blob}} object refers to a byte sequence, 276 | and has a {{Blob/size}} attribute which is the total number of bytes in the byte sequence, 277 | and a {{Blob/type}} attribute, 278 | which is an ASCII-encoded string in lower case representing the media type of the byte sequence. 279 | 280 | Each {{Blob}} must have an internal snapshot state, 281 | which must be initially set to the state of the underlying storage, 282 | if any such underlying storage exists. 283 | Further normative definition of snapshot state can be found for {{File}}s. 284 | 285 | 286 | [Exposed=(Window,Worker), Serializable] 287 | interface Blob { 288 | constructor(optional sequence<BlobPart> blobParts, 289 | optional BlobPropertyBag options = {}); 290 | 291 | readonly attribute unsigned long long size; 292 | readonly attribute DOMString type; 293 | 294 | // slice Blob into byte-ranged chunks 295 | Blob slice(optional [Clamp] long long start, 296 | optional [Clamp] long long end, 297 | optional DOMString contentType); 298 | 299 | // read from the Blob. 300 | [NewObject] ReadableStream stream(); 301 | [NewObject] Promise<USVString> text(); 302 | [NewObject] Promise<ArrayBuffer> arrayBuffer(); 303 | [NewObject] Promise<Uint8Array> bytes(); 304 | }; 305 | 306 | enum EndingType { "transparent", "native" }; 307 | 308 | dictionary BlobPropertyBag { 309 | DOMString type = ""; 310 | EndingType endings = "transparent"; 311 | }; 312 | 313 | typedef (BufferSource or Blob or USVString) BlobPart; 314 | 315 | 316 | {{Blob}} objects are [=serializable objects=]. Their [=serialization steps=], 317 | given |value| and |serialized|, are: 318 | 319 | 1. Set |serialized|.\[[SnapshotState]] to |value|'s [=snapshot state=]. 320 | 321 | 2. Set |serialized|.\[[ByteSequence]] to |value|'s underlying byte sequence. 322 | 323 | Their [=deserialization step=], given |serialized| and |value|, are: 324 | 325 | 1. Set |value|'s [=snapshot state=] to |serialized|.\[[SnapshotState]]. 326 | 327 | 2. Set |value|'s underlying byte sequence to |serialized|.\[[ByteSequence]]. 328 | 329 |
330 | A {{Blob}} |blob| has an associated get stream algorithm, 331 | which runs these steps: 332 | 333 | 1. Let |stream| be a [=new=] {{ReadableStream}} created in |blob|'s [=relevant Realm=]. 334 | 1. [=ReadableStream/set up with byte reading support|Set up=] |stream| with byte reading support. 335 | 1. Run the following steps [=in parallel=]: 336 | 1. While not all bytes of |blob| have been read: 337 | 1. Let |bytes| be the [=byte sequence=] that results from reading a [=chunk=] from |blob|, or 338 | failure if a chunk cannot be read. 339 | 1. [=Queue a global task=] on the [=file reading task source=] given |blob|'s 340 | [=relevant global object=] to perform the following steps: 341 | 1. If |bytes| is failure, then [=ReadableStream/error=] |stream| with a [=failure reason=] and 342 | abort these steps. 343 | 1. Let |chunk| be a new {{Uint8Array}} wrapping an {{ArrayBuffer}} containing |bytes|. If 344 | creating the {{ArrayBuffer}} throws an exception, then [=ReadableStream/error=] |stream| 345 | with that exception and abort these steps. 346 | 1. [=ReadableStream/Enqueue=] |chunk| in |stream|. 347 | 348 | Issue: We need to specify more concretely what reading from a Blob actually does, 349 | what possible errors can happen, perhaps something about chunk sizes, etc. 350 | 1. Return |stream|. 351 | 352 |
353 | 354 | ## Constructors ## {#constructorBlob} 355 | 356 |
357 | The {{Blob()}} constructor can be invoked with zero or more parameters. 358 | When the {{Blob()}} constructor is invoked, 359 | user agents must run the following steps: 360 | 361 | 1. If invoked with zero parameters, 362 | return a new {{Blob}} object consisting of 0 bytes, 363 | with {{Blob/size}} set to 0, 364 | and with {{Blob/type}} set to the empty string. 365 | 366 | 1. Let |bytes| be the result of [=processing blob parts=] given {{blobParts}} and {{Blob/Blob(blobParts, options)/options}}. 367 | 368 | 1. If the {{BlobPropertyBag/type}} member of the {{Blob/Blob(blobParts, options)/options}} argument is not the empty string, 369 | run the following sub-steps: 370 | 371 | 1. Let |t| be the {{BlobPropertyBag/type}} dictionary member. 372 | If |t| contains any characters outside the range U+0020 to U+007E, 373 | then set |t| to the empty string and return from these substeps. 374 | 1. Convert every character in |t| to [=ASCII lowercase=]. 375 | 376 | 1. Return a {{Blob}} object referring to |bytes| as its associated byte sequence, 377 | with its {{Blob/size}} set to the length of |bytes|, 378 | and its {{Blob/type}} set to the value of |t| from the substeps above. 379 | 380 |
381 | 382 | ### Constructor Parameters ### {#constructorParams} 383 | 384 | The {{Blob()}} constructor can be invoked with the parameters below: 385 | 386 |
387 |
A blobParts sequence 388 |
which takes any number of the following types of elements, and in any order: 389 | * {{BufferSource}} elements. 390 | * {{Blob}} elements. 391 | * {{USVString}} elements. 392 | 393 |
An *optional* {{BlobPropertyBag}} 394 |
which takes these optional members: 395 | * type, 396 | the ASCII-encoded string in lower case representing the media type of the {{Blob}}. 397 | Normative conditions for this member are provided in the [[#constructorBlob]]. 398 | * endings, 399 | an enum which can take the values {{"transparent"}} or {{"native"}}. 400 | By default this is set to {{"transparent"}}. If set to {{"native"}}, 401 | [=convert line endings to native|line endings will be converted to native=] 402 | in any {{USVString}} elements in {{blobParts}}. 403 |
404 | 405 |
406 | To process blob parts given a sequence of {{BlobPart}}'s |parts| 407 | and {{BlobPropertyBag}} |options|, 408 | run the following steps: 409 | 410 | 1. Let |bytes| be an empty sequence of bytes. 411 | 412 | 1. For each |element| in |parts|: 413 | 414 | 1. If |element| is a {{USVString}}, run the following substeps: 415 | 416 | 1. Let |s| be |element|. 417 | 418 | 1. If the {{BlobPropertyBag/endings}} member of |options| is {{"native"}}, 419 | set |s| to the result of [=converting line endings to native=] of |element|. 420 | 421 | 1. Append the result of [=UTF-8 encoding=] |s| to |bytes|. 422 | 423 | Note: The algorithm from WebIDL [[WebIDL]] replaces unmatched surrogates in an invalid utf-16 string 424 | with U+FFFD replacement characters. 425 | Scenarios exist when the {{Blob}} constructor may result in some data loss 426 | due to lost or scrambled character sequences. 427 | 428 | 1. If |element| is a {{BufferSource}}, get 429 | a copy of the bytes held by the buffer source, and append those bytes to |bytes|. 430 | 431 | 1. If |element| is a {{Blob}}, 432 | append the bytes it represents to |bytes|. 433 | 434 | Note: The {{Blob/type}} of the {{Blob}} array element is ignored and will not affect {{Blob/type}} of returned 435 | {{Blob}} object. 436 | 437 | 1. Return |bytes|. 438 | 439 |
440 | 441 |
442 | To 443 | convert line endings to native in a [=string=] |s|, 444 | run the following steps: 445 | 446 | 1. Let |native line ending| be the [=code point=] U+000A LF. 447 | 448 | 1. If the underlying platform's conventions are 449 | to represent newlines as a carriage return and line feed sequence, 450 | set |native line ending| to the [=code point=] U+000D CR 451 | followed by the [=code point=] U+000A LF. 452 | 453 | 1. Set |result| to the empty [=string=]. 454 | 455 | 1. Let |position| be a [=position variable=] for |s|, 456 | initially pointing at the start of |s|. 457 | 458 | 1. Let |token| be the result of [=collecting a sequence of code points=] 459 | that are not equal to U+000A LF or U+000D CR 460 | from |s| given |position|. 461 | 462 | 1. Append |token| to |result|. 463 | 464 | 1. While |position| is not past the end of |s|: 465 | 466 | 1. If the [=code point=] at |position| within |s| equals U+000D CR: 467 | 468 | 1. Append |native line ending| to |result|. 469 | 470 | 1. Advance |position| by 1. 471 | 472 | 1. If |position| is not past the end of |s| 473 | and the [=code point=] at |position| within |s| equals U+000A LF 474 | advance |position| by 1. 475 | 476 | 1. Otherwise if the [=code point=] at |position| within |s| equals U+000A LF, 477 | advance |position| by 1 and append |native line ending| to |result|. 478 | 479 | 1. Let |token| be the result of [=collecting a sequence of code points=] 480 | that are not equal to U+000A LF or U+000D CR 481 | from |s| given |position|. 482 | 483 | 1. Append |token| to |result|. 484 | 485 | 1. Return |result|. 486 | 487 |
488 | 489 |
490 | Examples of constructor usage follow. 491 | 492 |
 493 |   // Create a new Blob object
 494 | 
 495 |   var a = new Blob();
 496 | 
 497 |   // Create a 1024-byte ArrayBuffer
 498 |   // buffer could also come from reading a File
 499 | 
 500 |   var buffer = new ArrayBuffer(1024);
 501 | 
 502 |   // Create ArrayBufferView objects based on buffer
 503 | 
 504 |   var shorts = new Uint16Array(buffer, 512, 128);
 505 |   var bytes = new Uint8Array(buffer, shorts.byteOffset + shorts.byteLength);
 506 | 
 507 |   var b = new Blob(["foobarbazetcetc" + "birdiebirdieboo"], {type: "text/plain;charset=utf-8"});
 508 | 
 509 |   var c = new Blob([b, shorts]);
 510 | 
 511 |   var a = new Blob([b, c, bytes]);
 512 | 
 513 |   var d = new Blob([buffer, b, c, bytes]);
 514 |   
515 |
516 | 517 | ## Attributes ## {#attributes-blob} 518 | 519 |
520 |
size 521 |
Returns the size of the byte sequence in number of bytes. 522 | On getting, conforming user agents must return the total number of bytes that can be read by a {{FileReader}} or {{FileReaderSync}} object, 523 | or 0 if the {{Blob}} has no bytes to be read. 524 | 525 |
type 526 |
The ASCII-encoded string in lower case representing the media type of the {{Blob}}. 527 | On getting, user agents must return the type of a {{Blob}} 528 | as an ASCII-encoded string in lower case, 529 | such that when it is converted to a byte sequence, 530 | it is a parsable MIME type, 531 | or the empty string – 0 bytes – if the type cannot be determined. 532 | 533 | The {{Blob/type}} attribute can be set by the web application itself through constructor invocation 534 | and through the {{Blob/slice()}} call; 535 | in these cases, further normative conditions for this attribute are in [[#constructorBlob]], 536 | [[#file-constructor]], 537 | and [[#slice-method-algo]] respectively. 538 | User agents can also determine the {{Blob/type}} of a {{Blob}}, 539 | especially if the byte sequence is from an on-disk file; 540 | in this case, further normative conditions are in the file type guidelines. 541 | 542 | Note: The type t of a {{Blob}} is considered a parsable MIME type, 543 | if performing the parse a MIME type algorithm to a byte sequence converted from 544 | the ASCII-encoded string representing the Blob object's type does not return failure. 545 | 546 | Note: Use of the {{Blob/type}} attribute informs the [=package data=] algorithm 547 | and determines the `Content-Type` header when [=/fetching=] [=blob URLs=]. 548 |
549 | 550 | ## Methods and Parameters ## {#methodsandparams-blob} 551 | 552 | ### The {{Blob/slice()}} method ### {#slice-method-algo} 553 | 554 |
555 | The slice() method 556 | returns a new {{Blob}} object with bytes ranging from the optional start parameter 557 | up to but not including the optional end parameter, and with a {{Blob/type}} attribute 558 | that is the value of the optional contentType parameter. It must act as follows: 559 | 560 | 1. Let sliceStart, sliceEnd, and sliceContentType be null. 561 | 562 | 1. If start is given, set sliceStart to start. 563 | 564 | 1. If end is given, set sliceEnd to end. 565 | 566 | 1. If contentType is given, set sliceContentType to contentType. 567 | 568 | 1. Return the result of slice blob given this, sliceStart, 569 | sliceEnd, and sliceContentType. 570 | 571 |
572 | The examples below illustrate the different types of {{slice()}} calls possible. Since the 573 | {{File}} interface inherits from the {{Blob}} interface, examples are based on the use of the {{File}} interface. 574 | 575 |
 576 |     // obtain input element through DOM
 577 | 
 578 |     var file = document.getElementById('file').files[0];
 579 |     if(file)
 580 |     {
 581 |       // create an identical copy of file
 582 |       // the two calls below are equivalent
 583 | 
 584 |       var fileClone = file.slice();
 585 |       var fileClone2 = file.slice(0, file.size);
 586 | 
 587 |       // slice file into 1/2 chunk starting at middle of file
 588 |       // Note the use of negative number
 589 | 
 590 |       var fileChunkFromEnd = file.slice(-(Math.round(file.size/2)));
 591 | 
 592 |       // slice file into 1/2 chunk starting at beginning of file
 593 | 
 594 |       var fileChunkFromStart = file.slice(0, Math.round(file.size/2));
 595 | 
 596 |       // slice file from beginning till 150 bytes before end
 597 | 
 598 |       var fileNoMetadata = file.slice(0, -150, "application/experimental");
 599 |     }
 600 |   
601 |
602 |
603 | 604 | ### The {{Blob/stream()}} method ### {#stream-method-algo} 605 | 606 | The stream() method, when invoked, must return 607 | the result of calling [=get stream=] on [=this=]. 608 | 609 | ### The {{Blob/text()}} method ### {#text-method-algo} 610 | 611 | The text() method, when invoked, must run these steps: 612 | 613 | 1. Let |stream| be the result of calling [=get stream=] on [=this=]. 614 | 1. Let |reader| be the result of [=get a reader|getting a reader=] from |stream|. 615 | If that threw an exception, return a new promise rejected with that exception. 616 | 1. Let |promise| be the result of [=read all bytes|reading all bytes=] from |stream| with |reader|. 617 | 1. Return the result of transforming |promise| by a fulfillment handler that returns the result of 618 | running [=UTF-8 decode=] on its first argument. 619 | 620 | Note: This is different from the behavior of {{FileReader/readAsText()}} to align better 621 | with the behavior of {{Body/text()|Fetch's text()}}. Specifically this method will always 622 | use UTF-8 as encoding, while {{FileReader}} can use a different encoding depending on 623 | the blob's type and passed in encoding name. 624 | 625 | ### The {{Blob/arrayBuffer()}} method ### {#arraybuffer-method-algo} 626 | 627 | The arrayBuffer() method, when invoked, must run these steps: 628 | 629 | 1. Let |stream| be the result of calling [=get stream=] on [=this=]. 630 | 1. Let |reader| be the result of [=get a reader|getting a reader=] from |stream|. 631 | If that threw an exception, return a new promise rejected with that exception. 632 | 1. Let |promise| be the result of [=read all bytes|reading all bytes=] from |stream| with |reader|. 633 | 1. Return the result of transforming |promise| by a fulfillment handler that returns 634 | a new {{ArrayBuffer}} whose contents are its first argument. 635 | 636 | ### The {{Blob/bytes()}} method ### {#bytes-method-algo} 637 | 638 | The bytes() method, when invoked, must run these steps: 639 | 640 | 1. Let |stream| be the result of calling [=get stream=] on [=this=]. 641 | 1. Let |reader| be the result of [=get a reader|getting a reader=] from |stream|. 642 | If that threw an exception, return a new promise rejected with that exception. 643 | 1. Let |promise| be the result of [=read all bytes|reading all bytes=] from |stream| with |reader|. 644 | 1. Return the result of transforming |promise| by a fulfillment handler that returns 645 | a new {{Uint8Array}} wrapping an {{ArrayBuffer}} containing its first argument. 646 | 647 | 656 | 657 | # The File Interface # {#file-section} 658 | 659 | A {{File}} object is a {{Blob}} object with a {{File/name}} attribute, which is a string; 660 | it can be created within the web application via a constructor, 661 | or is a reference to a byte sequence from a file from the underlying (OS) file system. 662 | 663 | If a {{File}} object is a reference to a byte sequence originating from a file on disk, 664 | then its snapshot state should be set to the state of the file on disk at the time the {{File}} object is created. 665 | 666 | Note: This is a non-trivial requirement to implement for user agents, 667 | and is thus not a *must* but a *should* [[RFC2119]]. 668 | User agents should endeavor to have a {{File}} object's snapshot state 669 | set to the state of the underlying storage on disk at the time the reference is taken. 670 | If the file is modified on disk following the time a reference has been taken, 671 | the {{File}}'s snapshot state will differ from the state of the underlying storage. 672 | User agents may use modification time stamps and other mechanisms to maintain snapshot state, 673 | but this is left as an implementation detail. 674 | 675 | When a {{File}} object refers to a file on disk, 676 | user agents must return the {{Blob/type}} of that file, 677 | and must follow the file type guidelines below: 678 | 679 | * User agents must return the {{Blob/type}} as an ASCII-encoded string in lower case, 680 | such that when it is converted to a corresponding byte sequence, 681 | it is a parsable MIME type, 682 | or the empty string – 0 bytes – if the type cannot be determined. 683 | * When the file is of type text/plain 684 | user agents must NOT append a charset parameter to the dictionary of parameters portion of the media type [[!MIMESNIFF]]. 685 | * User agents must not attempt heuristic determination of encoding, 686 | including statistical methods. 687 | 688 |
 689 | [Exposed=(Window,Worker), Serializable]
 690 | interface File : Blob {
 691 |   constructor(sequence<BlobPart> fileBits,
 692 |               USVString fileName,
 693 |               optional FilePropertyBag options = {});
 694 |   readonly attribute DOMString name;
 695 |   readonly attribute long long lastModified;
 696 | };
 697 | 
 698 | dictionary FilePropertyBag : BlobPropertyBag {
 699 |   long long lastModified;
 700 | };
 701 | 
702 | 703 | {{File}} objects are [=serializable objects=]. Their [=serialization steps=], 704 | given |value| and |serialized|, are: 705 | 706 | 1. Set |serialized|.\[[SnapshotState]] to |value|'s [=snapshot state=]. 707 | 708 | 2. Set |serialized|.\[[ByteSequence]] to |value|'s underlying byte sequence. 709 | 710 | 3. Set |serialized|.\[[Name]] to the value of |value|'s {{File/name}} attribute. 711 | 712 | 4. Set |serialized|.\[[LastModified]] to the value of |value|'s {{File/lastModified}} attribute. 713 | 714 | Their [=deserialization steps=], given |value| and |serialized|, are: 715 | 716 | 1. Set |value|'s [=snapshot state=] to |serialized|.\[[SnapshotState]]. 717 | 718 | 2. Set |value|'s underlying byte sequence to |serialized|.\[[ByteSequence]]. 719 | 720 | 3. Initialize the value of |value|'s {{File/name}} attribute to |serialized|.\[[Name]]. 721 | 722 | 4. Initialize the value of |value|'s {{File/lastModified}} attribute to 723 | |serialized|.\[[LastModified]]. 724 | 725 | ## Constructor ## {#file-constructor} 726 | 727 |
728 | The {{File}} constructor is invoked with two or three parameters, 729 | depending on whether the optional dictionary parameter is used. 730 | When the {{File()}} constructor is invoked, 731 | user agents must run the following steps: 732 | 733 | 1. Let |bytes| be the result of [=processing blob parts=] given {{fileBits}} 734 | and {{File/File(fileBits, fileName, options)/options}}. 735 | 736 | 737 | 2. Let |n| be the {{fileName}} argument to the constructor. 738 | 739 | Note: Underlying OS filesystems use differing conventions for file name; 740 | with constructed files, mandating UTF-16 lessens ambiquity when file names are converted to byte sequences. 741 | 742 | 3. Process {{FilePropertyBag}} dictionary argument by running the following substeps: 743 | 744 | 1. If the {{BlobPropertyBag/type}} member is provided and is not the empty string, 745 | let |t| be set to the {{BlobPropertyBag/type}} dictionary member. 746 | If |t| contains any characters outside the range U+0020 to U+007E, 747 | then set |t| to the empty string and return from these substeps. 748 | 2. Convert every character in |t| to [=ASCII lowercase=]. 749 | 3. If the {{FilePropertyBag/lastModified}} member is provided, 750 | let |d| be set to the {{FilePropertyBag/lastModified}} dictionary member. 751 | If it is not provided, 752 | set |d| to the current date and time 753 | represented as the number of milliseconds since the Unix Epoch 754 | (which is the equivalent of Date.now() [[ECMA-262]]). 755 | 756 | Note: Since ECMA-262 {{Date}} objects convert to long long values 757 | representing the number of milliseconds since the Unix Epoch, 758 | the {{FilePropertyBag/lastModified}} member could be a {{Date}} object [[ECMA-262]]. 759 | 760 | 4. Return a new {{File}} object |F| such that: 761 | 2. |F| refers to the |bytes| byte sequence. 762 | 3. |F|.{{Blob/size}} is set to the number of total bytes in |bytes|. 763 | 4. |F|.{{File/name}} is set to |n|. 764 | 5. |F|.{{Blob/type}} is set to |t|. 765 | 6. |F|.{{File/lastModified}} is set to |d|. 766 | 767 |
768 | 769 | ### Constructor Parameters ### {#file-constructor-params} 770 | 771 | The {{File()}} constructor can be invoked with the parameters below: 772 | 773 |
774 |
A fileBits sequence 775 |
which takes any number of the following elements, and in any order: 776 | * {{BufferSource}} elements. 777 | * {{Blob}} elements, which includes {{File}} elements. 778 | * {{USVString}} elements. 779 | 780 |
A fileName parameter 781 |
A {{USVString}} parameter representing the name of the file; 782 | normative conditions for this constructor parameter can be found in [[#file-constructor]]. 783 | 784 |
An optional {{FilePropertyBag}} dictionary 785 |
which in addition to the members of 786 | {{BlobPropertyBag}} takes one member: 787 | * An optional lastModified member, 788 | which must be a long long; 789 | normative conditions for this member are provided in [[#file-constructor]]. 790 |
791 | 792 | ## Attributes ## {#file-attrs} 793 | 794 |
795 |
name 796 |
The name of the file. 797 | On getting, this must return the name of the file as a string. 798 | There are numerous file name variations and conventions used by different underlying OS file systems; 799 | this is merely the name of the file, without path information. 800 | On getting, if user agents cannot make this information available, 801 | they must return the empty string. 802 | If a {{File}} object is created using a constructor, 803 | further normative conditions for this attribute are found in [[#file-constructor]]. 804 | 805 |
lastModified 806 |
The last modified date of the file. 807 | On getting, if user agents can make this information available, 808 | this must return a long long set to the time the file was last modified 809 | as the number of milliseconds since the Unix Epoch. 810 | If the last modification date and time are not known, 811 | the attribute must return the current date and time 812 | as a long long representing the number of milliseconds since the Unix Epoch; 813 | this is equivalent to Date.now() [[ECMA-262]]. 814 | If a {{File}} object is created using a constructor, 815 | further normative conditions for this attribute are found in [[#file-constructor]]. 816 |
817 | 818 | The {{File}} interface is available on objects that expose an attribute of type {{FileList}}; 819 | these objects are defined in HTML [[HTML]]. 820 | The {{File}} interface, which inherits from {{Blob}}, is immutable, 821 | and thus represents file data that can be read into memory at the time a read operation is initiated. 822 | User agents must process reads on files that no longer exist at the time of read as errors, 823 | throwing a {{NotFoundError}} exception 824 | if using a {{FileReaderSync}} on a Web Worker [[Workers]] 825 | or firing an {{error!!event}} event 826 | with the {{error!!attribute}} attribute returning a {{NotFoundError}}. 827 | 828 |
829 | In the examples below, metadata from a file object is displayed meaningfully, and a file object is created with a name and a last modified date. 830 | 831 |
 832 |   var file = document.getElementById("filePicker").files[0];
 833 |   var date = new Date(file.lastModified);
 834 |   println("You selected the file " + file.name + " which was modified on " + date.toDateString() + ".");
 835 | 
 836 |   ...
 837 | 
 838 |   // Generate a file with a specific last modified date
 839 | 
 840 |   var d = new Date(2013, 12, 5, 16, 23, 45, 600);
 841 |   var generatedFile = new File(["Rough Draft ...."], "Draft1.txt", {type: "text/plain", lastModified: d})
 842 | 
 843 |   ...
 844 |   
845 |
846 | 847 | # The FileList Interface # {#filelist-section} 848 | 849 | Note: The {{FileList}} interface should be considered "at risk" 850 | since the general trend on the Web Platform is to replace such interfaces 851 | with the {{Array}} platform object in ECMAScript [[ECMA-262]]. 852 | In particular, this means syntax of the sort filelist.item(0) is at risk; 853 | most other programmatic use of {{FileList}} is unlikely to be affected by the eventual migration to an {{Array}} type. 854 | 855 | This interface is a list of {{File}} objects. 856 | 857 |
 858 | [Exposed=(Window,Worker), Serializable]
 859 | interface FileList {
 860 |   getter File? item(unsigned long index);
 861 |   readonly attribute unsigned long length;
 862 | };
 863 | 
864 | 865 | {{FileList}} objects are [=serializable objects=]. Their [=serialization steps=], 866 | given |value| and |serialized|, are: 867 | 868 | 1. Set |serialized|.\[[Files]] to an empty [=list=]. 869 | 870 | 2. For each |file| in |value|, append the [=sub-serialization=] of |file| to 871 | |serialized|.\[[Files]]. 872 | 873 | Their [=deserialization step=], given |serialized| and |value|, are: 874 | 875 | 1. [=list/For each=] |file| of |serialized|.\[[Files]], add the [=sub-deserialization=] of |file| to |value|. 876 | 877 |
878 | Sample usage typically involves DOM access to the <input type="file"> element within a form, 879 | and then accessing selected files. 880 | 881 |
 882 |     // uploadData is a form element
 883 |     // fileChooser is input element of type 'file'
 884 |     var file = document.forms['uploadData']['fileChooser'].files[0];
 885 | 
 886 |     // alternative syntax can be
 887 |     // var file = document.forms['uploadData']['fileChooser'].files.item(0);
 888 | 
 889 |     if(file)
 890 |     {
 891 |       // Perform file ops
 892 |     }
 893 |   
894 |
895 | 896 | ## Attributes ## {#attributes-filelist} 897 | 898 |
899 |
length 900 |
must return the number of files in the {{FileList}} object. 901 | If there are no files, this attribute must return 0. 902 |
903 | 904 | ## Methods and Parameters ## {#filelist-methods-params} 905 | 906 |
907 |
item(index) 908 |
must return the |index|th {{File}} object in the {{FileList}}. 909 | If there is no |index|th {{File}} object in the {{FileList}}, 910 | then this method must return null. 911 | 912 | index must be treated by user agents 913 | as value for the position of a {{File}} object in the {{FileList}}, 914 | with 0 representing the first file. 915 | Supported property indices are the numbers in the range zero 916 | to one less than the number of {{File}} objects represented by the {{FileList}} object. 917 | If there are no such {{File}} objects, 918 | then there are no supported property indices. 919 |
920 | 921 | Note: The {{HTMLInputElement}} interface has a readonly attribute of type {{FileList}}, 922 | which is what is being accessed in the above example. 923 | Other interfaces with a readonly attribute of type {{FileList}} include the {{DataTransfer}} interface. 924 | 925 | 926 | # Reading Data # {#reading-data-section} 927 | 928 | ## The File Reading Task Source ## {#blobreader-task-source} 929 | 930 | This specification defines a new generic [=task source=] called the 931 | file reading task source, 932 | which is used for all [=queue a task|tasks that are queued=] in this specification 933 | to read byte sequences associated with {{Blob}} and {{File}} objects. 934 | It is to be used for features that trigger in response to asynchronously reading binary data. 935 | 936 | ## The {{FileReader}} API ## {#APIASynch} 937 | 938 |
 939 | [Exposed=(Window,Worker)]
 940 | interface FileReader: EventTarget {
 941 |   constructor();
 942 |   // async read methods
 943 |   undefined readAsArrayBuffer(Blob blob);
 944 |   undefined readAsBinaryString(Blob blob);
 945 |   undefined readAsText(Blob blob, optional DOMString encoding);
 946 |   undefined readAsDataURL(Blob blob);
 947 | 
 948 |   undefined abort();
 949 | 
 950 |   // states
 951 |   const unsigned short EMPTY = 0;
 952 |   const unsigned short LOADING = 1;
 953 |   const unsigned short DONE = 2;
 954 | 
 955 |   readonly attribute unsigned short readyState;
 956 | 
 957 |   // File or Blob data
 958 |   readonly attribute (DOMString or ArrayBuffer)? result;
 959 | 
 960 |   readonly attribute DOMException? error;
 961 | 
 962 |   // event handler content attributes
 963 |   attribute EventHandler onloadstart;
 964 |   attribute EventHandler onprogress;
 965 |   attribute EventHandler onload;
 966 |   attribute EventHandler onabort;
 967 |   attribute EventHandler onerror;
 968 |   attribute EventHandler onloadend;
 969 | };
 970 | 
971 | 972 | A {{FileReader}} has an associated state, 973 | that is `"empty"`, `"loading"`, or `"done"`. It is initially `"empty"`. 974 | 975 | A {{FileReader}} has an associated result 976 | (`null`, a {{DOMString}} or an {{ArrayBuffer}}). It is initially `null`. 977 | 978 | A {{FileReader}} has an associated error 979 | (`null` or a {{DOMException}}). It is initially `null`. 980 | 981 | The FileReader() constructor, 982 | when invoked, must return a new {{FileReader}} object. 983 | 984 | The readyState attribute's getter, 985 | when invoked, switches on [=this=]'s [=FileReader/state=] 986 | and runs the associated step: 987 | 988 | : `"empty"` 989 | :: Return {{EMPTY}} 990 | : `"loading"` 991 | :: Return {{LOADING}} 992 | : `"done"` 993 | :: Return {{DONE}} 994 | 995 | The result attribute's getter, 996 | when invoked, must return [=this=]'s [=FileReader/result=]. 997 | 998 | The error attribute's getter, 999 | when invoked, must return [=this=]'s [=FileReader/error=]. 1000 | 1001 |
1002 | A {{FileReader}} |fr| has an associated read operation algorithm, 1003 | which given |blob|, a |type| and an optional |encodingName|, 1004 | runs the following steps: 1005 | 1006 | 1. If |fr|'s [=FileReader/state=] is `"loading"`, 1007 | throw an {{InvalidStateError}} {{DOMException}}. 1008 | 1. Set |fr|'s [=FileReader/state=] to `"loading"`. 1009 | 1. Set |fr|'s [=FileReader/result=] to `null`. 1010 | 1. Set |fr|'s [=FileReader/error=] to `null`. 1011 | 1. Let |stream| be the result of calling [=get stream=] on |blob|. 1012 | 1. Let |reader| be the result of [=get a reader|getting a reader=] from |stream|. 1013 | 1. Let |bytes| be an empty [=byte sequence=]. 1014 | 1. Let |chunkPromise| be the result of [=read a chunk|reading a chunk=] from |stream| with |reader|. 1015 | 1. Let |isFirstChunk| be true. 1016 | 1. [=In parallel=], while true: 1017 | 1. Wait for |chunkPromise| to be fulfilled or rejected. 1018 | 1. If |chunkPromise| is fulfilled, and |isFirstChunk| is true, 1019 | [=queue a task=] to [=fire a progress event=] called {{loadstart}} at |fr|. 1020 | 1021 | Issue(119): We might change {{loadstart}} to be dispatched synchronously, 1022 | to align with XMLHttpRequest behavior. 1023 | 1024 | 1. Set |isFirstChunk| to false. 1025 | 1026 | 1. If |chunkPromise| is fulfilled with an object whose `done` property is false and whose `value` 1027 | property is a `Uint8Array` object, run these steps: 1028 | 1. Let |bs| be the [=byte sequence=] represented by the `Uint8Array` object. 1029 | 1. Append |bs| to |bytes|. 1030 | 1. If roughly 50ms have passed since these steps were last invoked, 1031 | [=queue a task=] to [=fire a progress event=] called {{progress}} at |fr|. 1032 | 1. Set |chunkPromise| to the result of [=read a chunk|reading a chunk=] from |stream| with |reader|. 1033 | 1034 | 1. Otherwise, if |chunkPromise| is fulfilled with an object whose `done` property is true, 1035 | [=queue a task=] to run the following steps and abort this algorithm: 1036 | 1. Set |fr|'s [=FileReader/state=] to `"done"`. 1037 | 1. Let |result| be the result of 1038 | [=package data=] given |bytes|, |type|, |blob|'s {{Blob/type}}, and |encodingName|. 1039 | 1. If [=package data=] threw an exception |error|: 1040 | 1. Set |fr|'s [=FileReader/error=] to |error|. 1041 | 1. [=Fire a progress event=] called {{error!!event}} at |fr|. 1042 | 1. Else: 1043 | 1. Set |fr|'s [=FileReader/result=] to |result|. 1044 | 1. [=Fire a progress event=] called {{load}} at the |fr|. 1045 | 1. If |fr|'s [=FileReader/state=] is not `"loading"`, 1046 | [=fire a progress event=] called {{loadend}} at the |fr|. 1047 | 1048 | Note: Event handler for the {{load}} or {{error!!event}} events could have started another load, 1049 | if that happens the {{loadend}} event for this load is not fired. 1050 | 1051 | 1. Otherwise, if |chunkPromise| is rejected with an error |error|, 1052 | [=queue a task=] to run the following steps and abort this algorithm: 1053 | 1. Set |fr|'s [=FileReader/state=] to `"done"`. 1054 | 1. Set |fr|'s [=FileReader/error=] to |error|. 1055 | 1. [=Fire a progress event=] called {{error!!event}} at |fr|. 1056 | 1. If |fr|'s [=FileReader/state=] is not `"loading"`, 1057 | [=fire a progress event=] called {{loadend}} at |fr|. 1058 | 1059 | Note: Event handler for the {{error!!event}} event could have started another load, 1060 | if that happens the {{loadend}} event for this load is not fired. 1061 | 1062 | Use the [=file reading task source=] for all these tasks. 1063 |
1064 | 1065 | ### Event Handler Content Attributes ### {#event-handler-attributes-section} 1066 | 1067 | The following are the event handler content attributes 1068 | (and their corresponding event handler event types) 1069 | that user agents must support on {{FileReader}} as DOM attributes: 1070 | 1071 | 1072 | 1073 | 1074 | 1077 | 1078 | 1081 | 1084 | 1087 | 1090 | 1093 |
event handler content attribute 1075 | event handler event type 1076 |
onloadstart 1079 | {{loadstart}} 1080 |
onprogress 1082 | {{progress}} 1083 |
onabort 1085 | {{abort}} 1086 |
onerror 1088 | {{error!!event}} 1089 |
onload 1091 | {{load}} 1092 |
onloadend 1094 | {{loadend}} 1095 |
1096 | 1097 | ### FileReader States ### {#blobreader-state} 1098 | 1099 |
1100 | The {{FileReader}} object can be in one of 3 states. 1101 | The {{FileReader/readyState}} attribute tells you in which state the object is: 1102 | 1103 | : {{EMPTY}} (numeric value 0) 1104 | :: The {{FileReader}} object has been constructed, 1105 | and there are no pending reads. 1106 | None of the [=read methods=] have been called. 1107 | This is the default state of a newly minted {{FileReader}} object, 1108 | until one of the [=read methods=] have been called on it. 1109 | : {{LOADING}} (numeric value 1) 1110 | :: A {{File}} or {{Blob}} is being read. 1111 | One of the [=read methods=] is being processed, 1112 | and no error has occurred during the read. 1113 | : {{DONE}} (numeric value 2) 1114 | :: The entire {{File}} or {{Blob}} has been read into memory, 1115 | OR a [=file read error=] occurred, 1116 | OR the read was aborted using {{FileReader/abort()}}. 1117 | The {{FileReader}} is no longer reading a {{File}} or {{Blob}}. 1118 | If {{FileReader/readyState}} is set to {{FileReader/DONE}} 1119 | it means at least one of the [=read methods=] have been called on this {{FileReader}}. 1120 | 1121 |
1122 | 1123 | ### Reading a File or Blob ### {#reading-a-file} 1124 | 1125 | The {{FileReader}} interface makes available several asynchronous read methods-- 1126 | {{FileReader/readAsArrayBuffer()}}, {{FileReader/readAsBinaryString()}}, {{FileReader/readAsText()}} and {{FileReader/readAsDataURL()}}, 1127 | which read files into memory. 1128 | 1129 | Note: If multiple concurrent read methods are called on the same {{FileReader}} object, 1130 | user agents throw an {{InvalidStateError}} on any of the read methods that occur 1131 | when {{FileReader/readyState}} = {{FileReader/LOADING}}. 1132 | 1133 | ({{FileReaderSync}} makes available several [=synchronous read methods=]. 1134 | Collectively, the sync and async read methods of {{FileReader}} and {{FileReaderSync}} 1135 | are referred to as just read methods.) 1136 | 1137 | #### The {{FileReader/readAsDataURL()}} method #### {#readAsDataURL} 1138 | 1139 | The readAsDataURL(|blob|) method, 1140 | when invoked, must initiate a [=read operation=] for |blob| with *DataURL*. 1141 | 1142 | #### The {{FileReader/readAsText()}} method #### {#readAsDataText} 1143 | 1144 | The readAsText(|blob|, |encoding|) method, 1145 | when invoked, must initiate a [=read operation=] for |blob| with *Text* and |encoding|. 1146 | 1147 | #### The {{FileReader/readAsArrayBuffer()}} #### {#readAsArrayBuffer} 1148 | 1149 | The readAsArrayBuffer(|blob|) method, 1150 | when invoked, must initiate a [=read operation=] for |blob| with *ArrayBuffer*. 1151 | 1152 | #### The {{FileReader/readAsBinaryString()}} method #### {#readAsBinaryString} 1153 | 1154 | The readAsBinaryString(|blob|) method, 1155 | when invoked, must initiate a [=read operation=] for |blob| with *BinaryString*. 1156 | 1157 | Note: The use of {{FileReader/readAsArrayBuffer()}} is preferred over 1158 | {{FileReader/readAsBinaryString()}}, which is provided for backwards 1159 | compatibility. 1160 | 1161 | #### The {{FileReader/abort()}} method #### {#abort} 1162 | 1163 | When the abort() method is called, 1164 | the user agent must run the steps below: 1165 | 1166 | 1. If [=this=]'s [=FileReader/state=] is `"empty"` 1167 | or if [=this=]'s [=FileReader/state=] is `"done"` 1168 | set [=this=]'s [=FileReader/result=] to `null` 1169 | and [=terminate this algorithm=]. 1170 | 1. If [=this=]'s [=FileReader/state=] is `"loading"` 1171 | set [=this=]'s [=FileReader/state=] to `"done"` 1172 | and set [=this=]'s [=FileReader/result=] to `null`. 1173 | 1. If there are any [=tasks=] from [=this=] 1174 | on the [=file reading task source=] in an affiliated [=queue a task|task queue=], 1175 | then remove those [=tasks=] from that task queue. 1176 | 1. [=terminate an algorithm|Terminate the algorithm=] for the [=read method=] being processed. 1177 | 1. [=Fire a progress event=] called {{abort}} at [=this=]. 1178 | 1. If [=this=]'s [=FileReader/state=] is not `"loading"`, 1179 | [=fire a progress event=] called {{loadend}} at [=this=]. 1180 | 1181 | ## Packaging data ## {#packaging-data} 1182 | 1183 |
1184 | A {{Blob}} has an associated package data algorithm, 1185 | given |bytes|, a |type|, a optional |mimeType|, and a optional |encodingName|, 1186 | which switches on |type| and runs the associated steps: 1187 | 1188 | : DataURL 1189 | :: Return |bytes| as a DataURL [[!RFC2397]] subject to the considerations below: 1190 | 1191 | * Use |mimeType| as part of the Data URL if it is available 1192 | in keeping with the Data URL specification [[!RFC2397]]. 1193 | * If |mimeType| is not available return a Data URL without a media-type. [[!RFC2397]]. 1194 | 1195 | Issue(104): Better specify how the DataURL is generated. 1196 | 1197 | : Text 1198 | :: 1. Let |encoding| be failure. 1199 | 1. If the |encodingName| is present, set |encoding| to the result of 1200 | [=getting an encoding=] from |encodingName|. 1201 | 1. If |encoding| is failure, and |mimeType| is present: 1202 | 1. Let |type| be the result of [=parse a MIME type=] given |mimeType|. 1203 | 1. If |type| is not failure, 1204 | set |encoding| to the result of [=getting an encoding=] 1205 | from |type|'s [=MIME type/parameters=][`"charset"`]. 1206 | 1207 |
1208 | If `blob` has a {{Blob/type}} attribute of `text/plain;charset=utf-8` 1209 | then getting an encoding is run using `"utf-8"` as the label. 1210 | Note that user agents must parse and extract the portion of the Charset Parameter 1211 | that constitutes a *label* of an encoding. 1212 |
1213 | 1. If |encoding| is failure, then set |encoding| to [=UTF-8=]. 1214 | 1. [=Decode=] |bytes| using fallback encoding |encoding|, and return the result. 1215 | 1216 | : ArrayBuffer 1217 | :: Return a new `ArrayBuffer` whose contents are |bytes|. 1218 | 1219 | : BinaryString 1220 | :: Return |bytes| as a binary string, 1221 | in which every byte is represented by a code unit of equal value [0..255]. 1222 | 1223 |
1224 | 1225 | ## Events ## {#events} 1226 | 1227 | The {{FileReader}} object must be the event target for all events in this specification. 1228 | 1229 | When this specification says to fire a progress event called e 1230 | (for some {{ProgressEvent}} e 1231 | at a given {{FileReader}} reader), 1232 | the following are normative: 1233 | 1234 | * The progress event e does not bubble. e.bubbles must be false [[DOM]] 1235 | * The progress event e is NOT cancelable. e.cancelable must be false [[DOM]] 1236 | 1237 | ### Event Summary ### {#event-summary} 1238 | 1239 | The following are the events that are fired at {{FileReader}} objects. 1240 | 1241 | 1242 | 1243 | 1244 | 1248 | 1249 | 1253 | 1257 | 1262 | 1266 | 1270 |
Event name 1245 | Interface 1246 | Fired when… 1247 |
loadstart 1250 | {{ProgressEvent}} 1251 | When the read starts. 1252 |
progress 1254 | {{ProgressEvent}} 1255 | While reading (and decoding) blob 1256 |
abort 1258 | {{ProgressEvent}} 1259 | When the read has been aborted. 1260 | For instance, by invoking the {{FileReader/abort()}} method. 1261 |
error 1263 | {{ProgressEvent}} 1264 | When the read has failed (see file read errors). 1265 |
load 1267 | {{ProgressEvent}} 1268 | When the read has successfully completed. 1269 |
loadend 1271 | {{ProgressEvent}} 1272 | When the request has completed (either in success or failure). 1273 |
1274 | 1275 | ### Summary of Event Invariants ### {#eventInvariants} 1276 | 1277 | *This section is informative.* 1278 | 1279 | The following are invariants applicable to event firing 1280 | for a given asynchronous read method in this specification: 1281 | 1282 | 1. Once a {{loadstart}} has been fired, 1283 | a corresponding {{loadend}} fires at completion of the read, 1284 | UNLESS any of the following are true: 1285 | 1286 | * the read method has been cancelled using {{FileReader/abort()}} 1287 | and a new read method has been invoked 1288 | * the event handler function for a {{load}} event initiates a new read 1289 | * the event handler function for a {{error!!event}} event initiates a new read. 1290 | 1291 | Note: The events {{loadstart}} and {{loadend}} are not coupled in a one-to-one manner. 1292 | 1293 |
1294 | This example showcases "read-chaining": 1295 | initiating another read from within an event handler while the "first" read continues processing. 1296 | 1297 |
1298 |       // In code of the sort...
1299 |       reader.readAsText(file);
1300 |       reader.onload = function(){reader.readAsText(alternateFile);}
1301 | 
1302 |       .....
1303 | 
1304 |       //... the loadend event must not fire for the first read
1305 | 
1306 |       reader.readAsText(file);
1307 |       reader.abort();
1308 |       reader.onabort = function(){reader.readAsText(updatedFile);}
1309 | 
1310 |       //... the loadend event must not fire for the first read
1311 |     
1312 |
1313 | 2. One {{progress}} event will fire when blob has been completely read into memory. 1314 | 3. No {{progress}} event fires before {{loadstart}}. 1315 | 4. No {{progress}} event fires after any one of {{abort}}, {{load}}, and {{error!!event}} have fired. 1316 | At most one of {{abort}}, {{load}}, and {{error!!event}} fire for a given read. 1317 | 5. No {{abort}}, {{load}}, or {{error!!event}} event fires after {{loadend}}. 1318 | 1319 | ## Reading on Threads ## {#readingOnThreads} 1320 | 1321 | Web Workers allow for the use of synchronous {{File}} or {{Blob}} read APIs, 1322 | since such reads on threads do not block the main thread. 1323 | This section defines a synchronous API, which can be used within Workers [[Workers]]. 1324 | Workers can avail of both the asynchronous API (the {{FileReader}} object) 1325 | *and* the synchronous API (the {{FileReaderSync}} object). 1326 | 1327 | ### The {{FileReaderSync}} API ### {#FileReaderSync} 1328 | 1329 | This interface provides methods to synchronously read 1330 | {{File}} or {{Blob}} objects into memory. 1331 | 1332 |
1333 | [Exposed=(DedicatedWorker,SharedWorker)]
1334 | interface FileReaderSync {
1335 |   constructor();
1336 |   // Synchronously return strings
1337 | 
1338 |   ArrayBuffer readAsArrayBuffer(Blob blob);
1339 |   DOMString readAsBinaryString(Blob blob);
1340 |   DOMString readAsText(Blob blob, optional DOMString encoding);
1341 |   DOMString readAsDataURL(Blob blob);
1342 | };
1343 | 
1344 | 1345 | #### Constructors #### {#filereadersyncConstrctr} 1346 | 1347 | When the {{FileReaderSync()}} constructor is invoked, 1348 | the user agent must return a new {{FileReaderSync}} object. 1349 | 1350 | #### The {{FileReaderSync/readAsText()}} #### {#readAsTextSync} 1351 | 1352 | The readAsText(|blob|, |encoding|) method, 1353 | when invoked, must run these steps: 1354 | 1355 | 1. Let |stream| be the result of calling [=get stream=] on |blob|. 1356 | 1. Let |reader| be the result of [=get a reader|getting a reader=] from |stream|. 1357 | 1. Let |promise| be the result of [=read all bytes|reading all bytes=] from |stream| with |reader|. 1358 | 1. Wait for |promise| to be fulfilled or rejected. 1359 | 1. If |promise| fulfilled with a [=byte sequence=] |bytes|: 1360 | 1. Return the result of [=package data=] given |bytes|, *Text*, |blob|'s {{Blob/type}}, and |encoding|. 1361 | 1. Throw |promise|'s rejection reason. 1362 | 1363 | #### The {{FileReaderSync/readAsDataURL()}} method #### {#readAsDataURLSync-section} 1364 | 1365 | The readAsDataURL(|blob|) method, 1366 | when invoked, must run these steps: 1367 | 1368 | 1. Let |stream| be the result of calling [=get stream=] on |blob|. 1369 | 1. Let |reader| be the result of [=get a reader|getting a reader=] from |stream|. 1370 | 1. Let |promise| be the result of [=read all bytes|reading all bytes=] from |stream| with |reader|. 1371 | 1. Wait for |promise| to be fulfilled or rejected. 1372 | 1. If |promise| fulfilled with a [=byte sequence=] |bytes|: 1373 | 1. Return the result of [=package data=] given |bytes|, *DataURL*, and |blob|'s {{Blob/type}}. 1374 | 1. Throw |promise|'s rejection reason. 1375 | 1376 | #### The {{FileReaderSync/readAsArrayBuffer()}} method #### {#readAsArrayBufferSyncSection} 1377 | 1378 | The readAsArrayBuffer(|blob|) method, 1379 | when invoked, must run these steps: 1380 | 1381 | 1. Let |stream| be the result of calling [=get stream=] on |blob|. 1382 | 1. Let |reader| be the result of [=get a reader|getting a reader=] from |stream|. 1383 | 1. Let |promise| be the result of [=read all bytes|reading all bytes=] from |stream| with |reader|. 1384 | 1. Wait for |promise| to be fulfilled or rejected. 1385 | 1. If |promise| fulfilled with a [=byte sequence=] |bytes|: 1386 | 1. Return the result of [=package data=] given |bytes|, *ArrayBuffer*, and |blob|'s {{Blob/type}}. 1387 | 1. Throw |promise|'s rejection reason. 1388 | 1389 | #### The {{FileReaderSync/readAsBinaryString()}} method #### {#readAsBinaryStringSyncSection} 1390 | 1391 | The readAsBinaryString(|blob|) method, 1392 | when invoked, must run these steps: 1393 | 1394 | 1. Let |stream| be the result of calling [=get stream=] on |blob|. 1395 | 1. Let |reader| be the result of [=get a reader|getting a reader=] from |stream|. 1396 | 1. Let |promise| be the result of [=read all bytes|reading all bytes=] from |stream| with |reader|. 1397 | 1. Wait for |promise| to be fulfilled or rejected. 1398 | 1. If |promise| fulfilled with a [=byte sequence=] |bytes|: 1399 | 1. Return the result of [=package data=] given |bytes|, *BinaryString*, and |blob|'s {{Blob/type}}. 1400 | 1. Throw |promise|'s rejection reason. 1401 | 1402 | Note: The use of {{FileReaderSync/readAsArrayBuffer()}} is preferred over 1403 | {{FileReaderSync/readAsBinaryString()}}, which is provided for 1404 | backwards compatibility. 1405 | 1406 | # Errors and Exceptions # {#ErrorAndException} 1407 | 1408 | File read errors can occur when reading files from the underlying filesystem. 1409 | The list below of potential error conditions is *informative*. 1410 | 1411 | * The {{File}} or {{Blob}} being accessed may not exist 1412 | at the time one of the asynchronous read methods or synchronous read methods are called. 1413 | This may be due to it having been moved or deleted after a reference to it was acquired 1414 | (e.g. concurrent modification with another application). 1415 | See {{NotFoundError}}. 1416 | * A {{File}} or {{Blob}} may be unreadable. 1417 | This may be due to permission problems that occur after a reference to a {{File}} or {{Blob}} has been acquired 1418 | (e.g. concurrent lock with another application). 1419 | Additionally, the snapshot state may have changed. 1420 | See {{NotReadableError}}. 1421 | * User agents MAY determine that some files are unsafe for use within Web applications. 1422 | A file may change on disk since the original file selection, 1423 | thus resulting in an invalid read. 1424 | Additionally, some file and directory structures may be considered restricted by the underlying filesystem; 1425 | attempts to read from them may be considered a security violation. 1426 | See [[#security-discussion]] and {{SecurityError}}. 1427 | 1428 | ## Throwing an Exception or Returning an Error ## {#dfn-error-codes} 1429 | 1430 | *This section is normative.* 1431 | 1432 | Error conditions can arise when reading a {{File}} or a {{Blob}}. 1433 | 1434 | The read operation can terminate due to error conditions when reading a {{File}} or a {{Blob}}; 1435 | the particular error condition that causes the [=get stream=] algorithm to fail 1436 | is called a failure reason. A failure reason is one of 1437 | [=NotFound=], [=UnsafeFile=], [=TooManyReads=], [=SnapshotState=], or [=FileLock=]. 1438 | 1439 | Synchronous read methods throw exceptions of the type in the table below 1440 | if there has been an error owing to a particular failure reason. 1441 | 1442 | Asynchronous read methods use the {{FileReader/error!!attribute}} attribute of the {{FileReader}} object, 1443 | which must return a {{DOMException}} object of the most appropriate type from the table below 1444 | if there has been an error owing to a particular failure reason, 1445 | or otherwise return null. 1446 | 1447 | 1448 | 1449 | 1450 | 1453 | 1454 | 1461 | 1471 |
Type 1451 | Description and Failure Reason 1452 |
{{NotFoundError}} 1455 | If the {{File}} or {{Blob}} resource could not be found at the time the read was processed, 1456 | this is the NotFound failure reason. 1457 | 1458 | For asynchronous read methods the {{error!!attribute}} attribute must return a {{NotFoundError}} exception 1459 | and synchronous read methods must throw a {{NotFoundError}} exception. 1460 |
{{SecurityError}} 1462 | If: 1463 | * it is determined that certain files are unsafe for access within a Web application, this is the UnsafeFile failure reason. 1464 | * it is determined that too many read calls are being made on {{File}} or {{Blob}} resources, this is the TooManyReads failure reason. 1465 | 1466 | For asynchronous read methods the {{error!!attribute}} attribute may return a {{SecurityError}} exception 1467 | and synchronous read methods may throw a {{SecurityError}} exception. 1468 | 1469 | This is a security error to be used in situations not covered by any other failure reason. 1470 |
{{NotReadableError}} 1472 | If: 1473 | * the snapshot state of a {{File}} or a {{Blob}} does not match the state of the underlying storage, 1474 | this is the SnapshotState failure reason. 1475 | * the {{File}} or {{Blob}} cannot be read, 1476 | typically due due to permission problems that occur after a snapshot state has been established 1477 | (e.g. concurrent lock on the underlying storage with another application) 1478 | then this is the FileLock failure reason. 1479 | 1480 | For asynchronous read methods the {{error!!attribute}} attribute must return a {{NotReadableError}} exception 1481 | and synchronous read methods must throw a {{NotReadableError}} exception. 1482 |
1483 | 1484 | # A URL for Blob and MediaSource reference # {#url} 1485 | 1486 | This section defines a [=url/scheme=] for a [=/URL=] used to refer to {{Blob}} 1487 | and {{MediaSource}} objects. 1488 | 1489 | ## Introduction ## {#url-intro} 1490 | 1491 | *This section is informative.* 1492 | 1493 | [=Blob URL|Blob (or object) URLs=] are URLs like 1494 | `blob:http://example.com/550e8400-e29b-41d4-a716-446655440000`. 1495 | This enables integration of {{Blob}}s and {{MediaSource}}s with other 1496 | APIs that are only designed to be used with URLs, such as the <{img}> element. 1497 | [=Blob URLs=] can also be used to navigate to as well as to trigger downloads 1498 | of locally generated data. 1499 | 1500 | For this purpose two static methods are exposed on the {{URL}} interface, 1501 | {{URL/createObjectURL(obj)}} and {{URL/revokeObjectURL(url)}}. 1502 | The first method creates a mapping from a [=/URL=] to a {{Blob}}, 1503 | and the second method revokes said mapping. 1504 | As long as the mapping exist the {{Blob}} can't be garbage collected, 1505 | so some care must be taken to revoke the URL as soon as the reference is no longer needed. 1506 | All URLs are revoked when the global that created the URL itself goes away. 1507 | 1508 | ## Model ## {#url-model} 1509 | 1510 | Each user agent must maintain a blob URL store. 1511 | A [=blob URL store=] is a [=map=] 1512 | where [=map/keys=] are [=valid URL strings=] 1513 | and [=map/values=] are [=blob URL Entries=]. 1514 | 1515 | A blob URL entry consists of an object (of type 1516 | {{Blob}} or {{MediaSource}}), and an environment (an 1517 | [=environment settings object=]). 1518 | 1519 | Note: Specifications have to use the [=obtain a blob object=] algorithm to access a 1520 | [=blob URL entry=]'s [=blob URL entry/object=]. 1521 | 1522 | [=map/Keys=] in the [=blob URL store=] (also known as blob URLs) 1523 | are [=valid URL strings=] that when [=URL parser|parsed=] 1524 | result in a [=/URL=] with a [=url/scheme=] equal to "`blob`", 1525 | an [=empty host=], and a [=url/path=] consisting of one element itself also a [=valid URL string=]. 1526 | 1527 |
1528 | To obtain a blob object given a [=blob URL entry=] 1529 | |blobUrlEntry| and an [=environment settings object=], "`top-level-navigation`", or 1530 | "`top-level-self-fetch`" |environment|, perform the following steps. They return an 1531 | [=blob URL entry/object=]. 1532 | 1533 | 1. Let |isAuthorized| be true. 1534 | 1. If |environment| is an [=environment settings object=], then set |isAuthorized| to the result of 1535 | [=checking for same-partition blob URL usage=] with |blobUrlEntry| and |environment|. 1536 | 1. If |isAuthorized| is false, then return failure. 1537 | 1. Return |blobUrlEntry|'s [=blob URL entry/object=]. 1538 | 1539 |
1540 | 1541 |
1542 | To 1543 | generate a new blob URL, run the following steps: 1544 | 1545 | 1. Let |result| be the empty string. 1546 | 1. Append the string "`blob:`" to |result|. 1547 | 1. Let |settings| be the [=current settings object=] 1548 | 1. Let |origin| be |settings|'s [=environment settings object/origin=]. 1549 | 1. Let |serialized| be the ASCII serialization of |origin|. 1550 | 1. If |serialized| is "`null`", set it to an implementation-defined value. 1551 | 1. Append |serialized| to |result|. 1552 | 1. Append U+0024 SOLIDUS (`/`) to |result|. 1553 | 1. Generate a UUID [[!RFC4122]] as a string and append it to |result|. 1554 | 1. Return |result|. 1555 | 1556 |
1557 | 1558 |
1559 | An example of a blob URL that can be generated by this algorithm is 1560 | `blob:https://example.org/40a5fb5a-d56d-4a33-b4e2-0acf6a8e5f64`. 1561 |
1562 | 1563 |
1564 | To 1565 | add an entry to the blob URL store for a given |object|, 1566 | run the following steps: 1567 | 1568 | 1. Let |store| be the user agent's [=blob URL store=]. 1569 | 1. Let |url| be the result of [=generating a new blob URL=]. 1570 | 1. Let |entry| be a new [=blob URL entry=] consisting of |object| and the [=current settings object=]. 1571 | 1. [=map/Set=] |store|[|url|] to |entry|. 1572 | 1. Return |url|. 1573 | 1574 |
1575 | 1576 |
1577 | To 1578 | remove an entry from the blob URL store for a given |url|, 1579 | run the following steps: 1580 | 1581 | 1. Let |store| be the user agent's [=blob URL store=]; 1582 | 1. Let |url string| be the result of [=URL serializer|serializing=] |url|. 1583 | 1. [=map/Remove=] |store|[|url string|]. 1584 | 1585 |
1586 | 1587 | ## Dereferencing Model for blob URLs ## {#requestResponseModel} 1588 | 1589 |
1590 | To resolve a blob URL given a [=URL=] |url|: 1591 | 1592 | 1. [=Assert=]: |url|'s [=url/scheme=] is "`blob`". 1593 | 1. Let |store| be the user agent's [=blob URL store=]. 1594 | 1. Let |url string| be the result of [=URL serializer|serializing=] |url| with the *exclude fragment flag* set. 1595 | 1. If |store|[|url string|] [=map/exists=], return |store|[|url string|]; otherwise return failure. 1596 | 1597 |
1598 | 1599 | Futher requirements for the parsing and fetching model for [=blob URLs=] are defined in the [[!URL]] and [[!Fetch]] specifications. 1600 | 1601 | ### Origin of blob URLs ### {#originOfBlobURL} 1602 | 1603 | *This section is informative.* 1604 | 1605 | The origin of a blob URL is always the same as that of the environment that created the URL, 1606 | as long as the URL hasn't been revoked yet. This is achieved by the [[URL]] spec looking up 1607 | the URL in the [=blob URL store=] when parsing a URL, and using that entry to return 1608 | the correct origin. 1609 | 1610 | If the URL was revoked the serialization of the origin will still remain the same 1611 | as the serialization of the origin of the environment that created the blob URL, 1612 | but for opaque origins the origin itself might be distinct. This difference isn't 1613 | observable though, since a revoked blob URL can't be resolved/fetched anymore anyway. 1614 | 1615 | ### Access restrictions on blob URLs ### {#partitioningOfBlobUrls} 1616 | 1617 | Blob URLs can only be fetched from environments where the [=storage key=] matches that of 1618 | the environment where the blob URL was created. Blob URL navigations are not subject 1619 | to this restriction. 1620 | 1621 |
1622 | To check for same-partition blob URL usage given a [=blob URL entry=] |blobUrlEntry| and an [=environment settings object=] |environment|, perform the following steps. They return a boolean. 1623 | 1624 | 1. Let |blobStorageKey| be the result of [=obtaining a storage key for non-storage purposes=] with |blobUrlEntry|'s [=blob URL entry/environment=]. 1625 | 1. Let |environmentStorageKey| be the result of [=obtaining a storage key for non-storage purposes=] with |environment|. 1626 | 1. If |blobStorageKey| is not [=storage key/equal=] to |environmentStorageKey|, then return false. 1627 | 1. Return true. 1628 | 1629 |
1630 | 1631 |

Lifetime of blob URLs

1632 | 1633 | This specification extends the [=unloading document cleanup steps=] with the following steps: 1634 | 1635 | 1. Let |environment| be the {{Document}}'s [=relevant settings object=]. 1636 | 1. Let |store| be the user agent's [=blob URL store=]; 1637 | 1. Remove from |store| any entries for which the [=map/value=]'s [=blob URL entry/environment=] is equal to |environment|. 1638 | 1639 | Issue: This needs a similar hook when a worker is unloaded. 1640 | 1641 | ## Creating and Revoking a blob URL ## {#creating-revoking} 1642 | 1643 | Blob URLs are created and revoked using static methods exposed on the {{URL}} object. 1644 | Revocation of a blob URL decouples the blob URL from the resource it refers to, 1645 | and if it is dereferenced after it is revoked, 1646 | user agents must act as if a network error has occurred. 1647 | This section describes a supplemental interface to the URL specification [[URL]] 1648 | and presents methods for blob URL creation and revocation. 1649 | 1650 |
1651 | [Exposed=(Window,DedicatedWorker,SharedWorker)]
1652 | partial interface URL {
1653 |   static DOMString createObjectURL((Blob or MediaSource) obj);
1654 |   static undefined revokeObjectURL(DOMString url);
1655 | };
1656 | 
1657 | 1658 |
1659 | The createObjectURL(|obj|) static method must 1660 | return the result of [=adding an entry to the blob URL store=] for |obj|. 1661 |
1662 | 1663 |
1664 | The revokeObjectURL(|url|) static method must run these steps: 1665 | 1666 | 1. Let |urlRecord| be the result of [=URL parser|parsing=] |url|. 1667 | 1. If |urlRecord|'s [=url/scheme=] is not "`blob`", return. 1668 | 1. Let |entry| be |urlRecord|'s [=blob URL entry=]. 1669 | 1. If |entry| is null, then return. 1670 | 1. Let |isAuthorized| be the result of [=checking for same-partition blob URL usage=] with |entry| and the [=current settings object=]. 1671 | 1. If |isAuthorized| is false, then return. 1672 | 1. [=Remove an entry from the Blob URL Store=] for |url|. 1673 | 1674 | Note: This means that rather than throwing some kind of error, attempting to revoke a URL that isn't registered or that was registered from an environment in a different storage partition will silently fail. 1675 | User agents might display a message on the error console if this happens. 1676 | 1677 | Note: Attempts to dereference |url| after it has been revoked will result in a [=network error=]. 1678 | Requests that were started before the |url| was revoked should still succeed. 1679 |
1680 | 1681 |
1682 | In the example below, 1683 | window1 and window2 are separate, 1684 | but in the same origin; 1685 | window2 could be an <{iframe}> inside window1. 1686 | 1687 |
1688 |     myurl = window1.URL.createObjectURL(myblob);
1689 |     window2.URL.revokeObjectURL(myurl);
1690 |   
1691 | 1692 | Since a user agent has one global [=blob URL store=], 1693 | it is possible to revoke an object URL from a different window than from which it was created. 1694 | The URL.{{revokeObjectURL()}} call 1695 | ensures that subsequent dereferencing of myurl 1696 | results in a the user agent acting as if a network error has occurred. 1697 |
1698 | 1699 | ### Examples of blob URL Creation and Revocation ### {#examplesOfCreationRevocation} 1700 | 1701 | Blob URLs are strings that are used to [=/fetch=] {{Blob}} objects, 1702 | and can persist for as long as the document from which they were minted 1703 | using URL.{{createObjectURL()}}-- 1704 | see [[#lifeTime]]. 1705 | 1706 | This section gives sample usage of creation and revocation of blob URLs with explanations. 1707 | 1708 |
1709 | In the example below, two <{img}> elements [[HTML]] refer to the same blob URL: 1710 | 1711 |
1712 |     url = URL.createObjectURL(blob);
1713 |     img1.src = url;
1714 |     img2.src = url;
1715 |   
1716 |
1717 | 1718 |
1719 | In the example below, URL.{{revokeObjectURL()}} is explicitly called. 1720 | 1721 |
1722 |     var blobURLref = URL.createObjectURL(file);
1723 |     img1 = new Image();
1724 |     img2 = new Image();
1725 | 
1726 |     // Both assignments below work as expected
1727 |     img1.src = blobURLref;
1728 |     img2.src = blobURLref;
1729 | 
1730 |     // ... Following body load
1731 |     // Check if both images have loaded
1732 |     if(img1.complete && img2.complete) {
1733 |       // Ensure that subsequent refs throw an exception
1734 |       URL.revokeObjectURL(blobURLref);
1735 |     } else {
1736 |       msg("Images cannot be previewed!");
1737 |       // revoke the string-based reference
1738 |       URL.revokeObjectURL(blobURLref);
1739 |     }
1740 |   
1741 |
1742 | 1743 | The example above allows multiple references to a single blob URL, 1744 | and the web developer then revokes the blob URL string after both image objects have been loaded. 1745 | While not restricting number of uses of the blob URL offers more flexibility, 1746 | it increases the likelihood of leaks; 1747 | developers should pair it with a corresponding call to URL.{{revokeObjectURL()}}. 1748 | 1749 | # Security and Privacy Considerations # {#security-discussion} 1750 | 1751 | *This section is informative.* 1752 | 1753 | This specification allows web content to read files from the underlying file system, 1754 | as well as provides a means for files to be accessed by unique identifiers, 1755 | and as such is subject to some security considerations. 1756 | This specification also assumes that the primary user interaction is with the <input type="file"/> element of HTML forms [[HTML]], 1757 | and that all files that are being read by {{FileReader}} objects have first been selected by the user. 1758 | Important security considerations include preventing malicious file selection attacks (selection looping), 1759 | preventing access to system-sensitive files, 1760 | and guarding against modifications of files on disk after a selection has taken place. 1761 | 1762 | : Preventing selection looping 1763 | :: During file selection, a user may be bombarded with the file picker associated with <input type="file"/> 1764 | (in a "must choose" loop that forces selection before the file picker is dismissed) 1765 | and a user agent may prevent file access to any selections by making the {{FileList}} object returned be of size 0. 1766 | : System-sensitive files 1767 | :: (e.g. files in /usr/bin, password files, and other native operating system executables) 1768 | typically should not be exposed to web content, 1769 | and should not be accessed via blob URLs. 1770 | User agents may throw a {{SecurityError}} exception for synchronous read methods, 1771 | or return a {{SecurityError}} exception for asynchronous reads. 1772 | 1773 | Issue: This section is provisional; more security data may supplement this in subsequent drafts. 1774 | 1775 | # Requirements and Use Cases # {#requirements} 1776 | 1777 | This section covers what the requirements are for this API, 1778 | as well as illustrates some use cases. 1779 | This version of the API does not satisfy all use cases; 1780 | subsequent versions may elect to address these. 1781 | 1782 | * Once a user has given permission, 1783 | user agents should provide the ability to read and parse data directly from a local file programmatically. 1784 | 1785 |
1786 | A lyrics viewer. 1787 | User wants to read song lyrics from songs in his plist file. 1788 | User browses for plist file. 1789 | File is opened, read, parsed, and presented to the user as a sortable, actionable list within a web application. 1790 | User can select songs to fetch lyrics. 1791 | User uses the "browse for file" dialog. 1792 |
1793 | * Data should be able to be stored locally so that it is available for later use, 1794 | which is useful for offline data access for web applications. 1795 | 1796 |
1797 | A Calendar App. 1798 | User's company has a calendar. 1799 | User wants to sync local events to company calendar, 1800 | marked as "busy" slots (without leaking personal info). 1801 | User browses for file and selects it. 1802 | The text/calendar file is parsed in the browser, 1803 | allowing the user to merge the files to one calendar view. 1804 | The user wants to then save the file back to his local calendar file (using "Save As"?). 1805 | The user can also send the integrated calendar file back to the server calendar store asynchronously. 1806 |
1807 | * User agents should provide the ability to save a local file programmatically given an amount of data and a file name. 1808 | 1809 | Note: While this specification doesn't provide an explicit API call to trigger downloads, 1810 | the HTML5 specification has addressed this. 1811 | The <{a/download}> attribute of the <{a}> element initiates a download, 1812 | saving a {{File}} with the name specified. 1813 | The combination of this API and the <{a/download}> attribute on <{a}> elements 1814 | allows for the creation of files within web applications, 1815 | and the ability to save them locally. 1816 | 1817 |
1818 | A Spreadsheet App. 1819 | User interacts with a form, and generates some input. 1820 | The form then generates a CSV (Comma Separated Variables) output for the user to import into a spreadsheet, 1821 | and uses "Save...". 1822 | The generated output can also be directly integrated into a web-based spreadsheet, 1823 | and uploaded asynchronously. 1824 |
1825 | * User agents should provide a streamlined programmatic ability to send data from a file to a remote server 1826 | that works more efficiently than form-based uploads today. 1827 | 1828 |
1829 | A Video/Photo Upload App. 1830 | User is able to select large files for upload, 1831 | which can then be "chunk-transfered" to the server. 1832 |
1833 | * User agents should provide an API exposed to script that exposes the features above. 1834 | The user is notified by UI anytime interaction with the file system takes place, 1835 | giving the user full ability to cancel or abort the transaction. 1836 | The user is notified of any file selections, 1837 | and can cancel these. 1838 | No invocations to these APIs occur silently without user intervention. 1839 | 1840 |

1841 | Acknowledgements

1842 | 1843 | This specification was originally developed by the SVG Working Group. Many thanks to Mark Baker and Anne van Kesteren for their feedback. 1844 | 1845 | Thanks to Robin Berjon, Jonas Sicking and Vsevolod Shmyroff for editing the original specification. 1846 | 1847 | Special thanks to Olli Pettay, Nikunj Mehta, Garrett Smith, Aaron Boodman, Michael Nordman, Jian Li, Dmitry Titov, Ian Hickson, Darin Fisher, Sam Weinig, Adrian Bateman and Julian Reschke. 1848 | 1849 | Thanks to the W3C WebApps WG, and to participants on the public-webapps@w3.org listserv 1850 | --------------------------------------------------------------------------------