hwq пре 4 година
родитељ
комит
2a225525a6
100 измењених фајлова са 11558 додато и 588 уклоњено
  1. 23 0
      api/apply.js
  2. 10 0
      api/index.js
  3. 5 0
      exif-js/.npmignore
  4. 9 0
      exif-js/CHANGELOG.md
  5. 21 0
      exif-js/LICENSE.md
  6. 87 0
      exif-js/README.md
  7. 23 0
      exif-js/bower.json
  8. BIN
      exif-js/example/Bloated-Hero.jpg
  9. BIN
      exif-js/example/Bush-dog.jpg
  10. BIN
      exif-js/example/DSCN0614_small.jpg
  11. BIN
      exif-js/example/dsc_09827.jpg
  12. 53 0
      exif-js/example/index.html
  13. 10 0
      exif-js/exif.d.ts
  14. 1059 0
      exif-js/exif.js
  15. 72 0
      exif-js/index.html
  16. 67 0
      exif-js/package.json
  17. BIN
      exif-js/spec/Exif2-2.pdf
  18. 1 549
      package-lock.json
  19. 62 37
      pages/homework/setHomework.vue
  20. 22 2
      pages/index/index.vue
  21. 23 0
      qiniu-js/.babelrc
  22. 5 0
      qiniu-js/.eslintignore
  23. 13 0
      qiniu-js/.eslintrc.js
  24. 22 0
      qiniu-js/.travis.yml
  25. 507 0
      qiniu-js/README.md
  26. 541 0
      qiniu-js/coverage/clover.xml
  27. 0 0
      qiniu-js/coverage/coverage-final.json
  28. 224 0
      qiniu-js/coverage/lcov-report/base.css
  29. 79 0
      qiniu-js/coverage/lcov-report/block-navigation.js
  30. BIN
      qiniu-js/coverage/lcov-report/favicon.png
  31. 141 0
      qiniu-js/coverage/lcov-report/index.html
  32. 1 0
      qiniu-js/coverage/lcov-report/prettify.css
  33. 1 0
      qiniu-js/coverage/lcov-report/prettify.js
  34. BIN
      qiniu-js/coverage/lcov-report/sort-arrow-sprite.png
  35. 170 0
      qiniu-js/coverage/lcov-report/sorter.js
  36. 533 0
      qiniu-js/coverage/lcov-report/src/api.ts.html
  37. 710 0
      qiniu-js/coverage/lcov-report/src/base64.ts.html
  38. 176 0
      qiniu-js/coverage/lcov-report/src/config.ts.html
  39. 668 0
      qiniu-js/coverage/lcov-report/src/image.ts.html
  40. 186 0
      qiniu-js/coverage/lcov-report/src/index.html
  41. 126 0
      qiniu-js/coverage/lcov-report/src/logger/index.html
  42. 263 0
      qiniu-js/coverage/lcov-report/src/logger/index.ts.html
  43. 224 0
      qiniu-js/coverage/lcov-report/src/logger/report-v3.ts.html
  44. 218 0
      qiniu-js/coverage/lcov-report/src/pool.ts.html
  45. 854 0
      qiniu-js/coverage/lcov-report/src/upload/base.ts.html
  46. 111 0
      qiniu-js/coverage/lcov-report/src/upload/index.html
  47. 1142 0
      qiniu-js/coverage/lcov-report/src/utils.ts.html
  48. 1000 0
      qiniu-js/coverage/lcov.info
  49. 0 0
      qiniu-js/dist/qiniu.min.js
  50. 0 0
      qiniu-js/dist/qiniu.min.js.map
  51. 1 0
      qiniu-js/esm/__tests__/api.test.d.ts
  52. 102 0
      qiniu-js/esm/__tests__/api.test.js
  53. 1 0
      qiniu-js/esm/__tests__/api.test.js.map
  54. 1 0
      qiniu-js/esm/__tests__/image.test.d.ts
  55. 53 0
      qiniu-js/esm/__tests__/image.test.js
  56. 1 0
      qiniu-js/esm/__tests__/image.test.js.map
  57. 1 0
      qiniu-js/esm/__tests__/pool.test.d.ts
  58. 29 0
      qiniu-js/esm/__tests__/pool.test.js
  59. 1 0
      qiniu-js/esm/__tests__/pool.test.js.map
  60. 1 0
      qiniu-js/esm/__tests__/utils.test.d.ts
  61. 8 0
      qiniu-js/esm/__tests__/utils.test.js
  62. 1 0
      qiniu-js/esm/__tests__/utils.test.js.map
  63. 54 0
      qiniu-js/esm/api.d.ts
  64. 143 0
      qiniu-js/esm/api.js
  65. 0 0
      qiniu-js/esm/api.js.map
  66. 5 0
      qiniu-js/esm/base64.d.ts
  67. 162 0
      qiniu-js/esm/base64.js
  68. 0 0
      qiniu-js/esm/base64.js.map
  69. 17 0
      qiniu-js/esm/compress.d.ts
  70. 248 0
      qiniu-js/esm/compress.js
  71. 0 0
      qiniu-js/esm/compress.js.map
  72. 31 0
      qiniu-js/esm/config.d.ts
  73. 33 0
      qiniu-js/esm/config.js
  74. 1 0
      qiniu-js/esm/config.js.map
  75. 48 0
      qiniu-js/esm/image.d.ts
  76. 134 0
      qiniu-js/esm/image.js
  77. 0 0
      qiniu-js/esm/image.js.map
  78. 21 0
      qiniu-js/esm/index.d.ts
  79. 39 0
      qiniu-js/esm/index.js
  80. 1 0
      qiniu-js/esm/index.js.map
  81. 31 0
      qiniu-js/esm/logger/index.d.ts
  82. 94 0
      qiniu-js/esm/logger/index.js
  83. 1 0
      qiniu-js/esm/logger/index.js.map
  84. 1 0
      qiniu-js/esm/logger/index.test.d.ts
  85. 124 0
      qiniu-js/esm/logger/index.test.js
  86. 0 0
      qiniu-js/esm/logger/index.test.js.map
  87. 19 0
      qiniu-js/esm/logger/report-v3.d.ts
  88. 34 0
      qiniu-js/esm/logger/report-v3.js
  89. 1 0
      qiniu-js/esm/logger/report-v3.js.map
  90. 1 0
      qiniu-js/esm/logger/report-v3.test.d.ts
  91. 115 0
      qiniu-js/esm/logger/report-v3.test.js
  92. 0 0
      qiniu-js/esm/logger/report-v3.test.js.map
  93. 71 0
      qiniu-js/esm/observable.d.ts
  94. 104 0
      qiniu-js/esm/observable.js
  95. 1 0
      qiniu-js/esm/observable.js.map
  96. 16 0
      qiniu-js/esm/pool.d.ts
  97. 40 0
      qiniu-js/esm/pool.js
  98. 1 0
      qiniu-js/esm/pool.js.map
  99. 111 0
      qiniu-js/esm/upload/base.d.ts
  100. 194 0
      qiniu-js/esm/upload/base.js

+ 23 - 0
api/apply.js

@@ -0,0 +1,23 @@
+import request from '@/utils/request'
+import { upFilse, upVideo} from '@/utils/request';
+
+
+//上传图片
+
+export function uploads(data){
+	return upFilse({
+		url:'/api/upload/image',
+		method:'post',
+		data
+	})
+}
+
+//上传视频
+
+export function upvideo(data){
+	return upVideo({
+		url:'/api/upload/image',
+		method:'post',
+		data
+	})
+}

+ 10 - 0
api/index.js

@@ -0,0 +1,10 @@
+import request from '@/utils/request'
+
+//获取首页信息
+export function banner(data) {
+	return request({
+		url: '/api/index',
+		method: 'get',
+		data
+	});
+}

+ 5 - 0
exif-js/.npmignore

@@ -0,0 +1,5 @@
+.*.swp
+.DS_Store
+/.vimproject
+/Session.vim
+/node_modules

+ 9 - 0
exif-js/CHANGELOG.md

@@ -0,0 +1,9 @@
+# Changelog
+
+## v2.1.1 07/08/2015
+
+*No changelog for this release.*
+
+## v2.2.0 31/03/2017
+
+Issue #69 #72: Added IPTC methods. New NPM release.

+ 21 - 0
exif-js/LICENSE.md

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

+ 87 - 0
exif-js/README.md

@@ -0,0 +1,87 @@
+# Exif.js
+
+A JavaScript library for reading [EXIF meta data](https://en.wikipedia.org/wiki/Exchangeable_image_file_format) from image files.
+
+You can use it on images in the browser, either from an image or a file input element. Both EXIF and IPTC metadata are retrieved.
+This package can also be used in AMD or CommonJS environments.
+
+**Note**: The EXIF standard applies only to `.jpg` and `.tiff` images. EXIF logic in this package is based on the EXIF standard v2.2 ([JEITA CP-3451, included in this repo](/spec/Exif2-2.pdf)).
+
+## Install
+Install `exif-js` through [NPM](https://www.npmjs.com/#getting-started):
+
+    npm install exif-js --save    
+
+Or [Bower](http://bower.io/):
+
+    bower install exif-js --save
+
+Then add a `script` tag in your an HTML in the [best position](http://stackoverflow.com/questions/436411/where-is-the-best-place-to-put-script-tags-in-html-markup) referencing your local file.
+
+    <script src="vendors/exif-js/exif-js"></script>
+
+**Note**: This repo has no `.min.js`. Do your own [minification](https://en.wikipedia.org/wiki/Minification_(programming)) if you want that.
+
+If you prefer another package manager you will probably manage :D. Or you can clone this GIT repository or download it's ZIP file and extract `exif.js` to your project.
+
+## Usage
+The package adds a global `EXIF` variable (or AMD or CommonJS equivalent).
+
+Start with calling the `EXIF.getData` function. You pass it an image as a parameter:
+- either an image from a `<img src="image.jpg">`
+- OR a user selected image in a `<file type="input">` element on your page.
+
+As a second parameter you specify a callback function. In the callback function you should use `this` to access the image with the aforementioned metadata you can then use as you want.
+That image now has an extra `exifdata` property which is a Javascript object with the EXIF metadata. You can access it's properties to get data like the *image caption*, the *date a photo was taken* or it's *orientation*.
+
+You can get all tages with `EXIF.getTag`. Or get a single tag with `EXIF.getTag`, where you specify the tag as the second parameter.
+The tag names to use are listed in `EXIF.Tags` in `exif.js`.
+
+**Important**: Note that you have to wait for the image to be completely loaded, before calling `getData` or any other function. It will silently fail otherwise.
+You can implement this wait, by running your exif-extracting logic on the `window.onLoad` function. Or on an image's own `onLoad` function.
+For jQuery users please note that you can NOT (reliably) use jQuery's `ready` event for this. Because it fires before images are loaded.
+You could use $(window).load() instead of $(document.ready() (please note that `exif-js has NO dependency on jQuery or any other external library). 
+ 
+**JavaScript**:
+```javascript
+window.onload=getExif;
+
+function getExif() {
+    var img1 = document.getElementById("img1");
+    EXIF.getData(img1, function() {
+        var make = EXIF.getTag(this, "Make");
+        var model = EXIF.getTag(this, "Model");
+        var makeAndModel = document.getElementById("makeAndModel");
+        makeAndModel.innerHTML = `${make} ${model}`;
+    });
+
+    var img2 = document.getElementById("img2");
+    EXIF.getData(img2, function() {
+        var allMetaData = EXIF.getAllTags(this);
+        var allMetaDataSpan = document.getElementById("allMetaDataSpan");
+        allMetaDataSpan.innerHTML = JSON.stringify(allMetaData, null, "\t");
+    });
+}
+```
+
+**HTML**:
+```html
+<img src="image1.jpg" id="img1" />
+<pre>Make and model: <span id="makeAndModel"></span></div>
+<br/>
+<img src="image2.jpg" id="img2" />
+<pre id="allMetaDataSpan"></pre>
+<br/>
+```
+
+Note there are also alternate tags, such the `EXIF.TiffTags`. See the source code for the full definition and use.
+You can also get back a string with all the EXIF information in the image pretty printed by using `EXIF.pretty`.
+Check the included [index.html](/exif-js/exif-js/blob/master/index.html).
+
+Please refer to the [source code](exif.js) for more advanced usages such as getting image data from a [File/Blob](https://developer.mozilla.org/en/docs/Web/API/Blob) object (`EXIF.readFromBinaryFile`).
+
+## Contributions
+This is an [open source project](LICENSE.md). Please contribute by forking this repo and issueing a pull request. The project has had notable contributions already, like reading ITPC data.
+
+You can also contribute by [filing bugs or new features please issue](/exif-js/issues).
+Or improve the documentation. Please update this README when you do a pull request of proposed changes in base functionality.

+ 23 - 0
exif-js/bower.json

@@ -0,0 +1,23 @@
+{
+  "name": "exif-js",
+  "version": "2.2.2",
+  "homepage": "https://github.com/exif-js/exif-js",
+  "authors": [
+    "Jacob Seidelin"
+  ],
+  "description": "JavaScript library for reading EXIF image metadata",
+  "main": "exif.js",
+  "keywords": [
+    "exif"
+  ],
+  "license": "MIT",
+  "ignore": [
+    "**/.*",
+    "node_modules",
+    "bower_components",
+    "test",
+    "tests",
+    "spec",
+    "example"
+  ]
+}

BIN
exif-js/example/Bloated-Hero.jpg


BIN
exif-js/example/Bush-dog.jpg


BIN
exif-js/example/DSCN0614_small.jpg


BIN
exif-js/example/dsc_09827.jpg


+ 53 - 0
exif-js/example/index.html

@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <meta http-equiv="X-UA-Compatible" content="ie=edge">
+    <title>EXIF example with inline EXIF info</title>
+</head>
+<body>
+
+    <img src="DSCN0614_small.jpg" id="img1" />
+    <pre>Make and model: <span id="makeAndModel"></span></div>
+    <br/>
+
+    <img src="Bush-dog.jpg" id="img2" />
+    <pre id="allMetaDataSpan"></pre>
+    <br/>
+
+    <img src="Bloated-Hero.jpg" id="img3" /><br/>
+    <pre id="img3WithXmpMetaData"></pre>
+
+    <script type="text/javascript" src="../exif.js"></script>
+    <script>
+        "use strict";
+        window.onload=getExif;
+
+        function getExif() {
+            var img1 = document.getElementById("img1");
+            EXIF.getData(img1, function() {
+                var make = EXIF.getTag(this, "Make");
+                var model = EXIF.getTag(this, "Model");
+                var makeAndModel = document.getElementById("makeAndModel");
+                makeAndModel.innerHTML = `${make} ${model}`;
+            });
+
+            var img2 = document.getElementById("img2");
+            EXIF.getData(img2, function() {
+                var allMetaData = EXIF.getAllTags(this);
+                var allMetaDataSpan = document.getElementById("allMetaDataSpan");
+                allMetaDataSpan.innerHTML = JSON.stringify(allMetaData, null, "\t");
+            });
+
+            var img3 = document.getElementById("img3");
+            // EXIF.enableXmp();
+            EXIF.getData(img3, function() {
+                var allMetaData = EXIF.getAllTags(this);
+                var img3WithXmpMetaData = document.getElementById("img3WithXmpMetaData");
+                img3WithXmpMetaData.innerHTML = JSON.stringify(allMetaData, null, "\t");
+            });
+        }
+    </script>
+</body>
+</html>

+ 10 - 0
exif-js/exif.d.ts

@@ -0,0 +1,10 @@
+interface EXIFStatic {
+    getData(url: string, callback: any): any;
+    getTag(img: any, tag: any): any;
+    getAllTags(img: any): any;
+    pretty(img: any): string;
+    readFromBinaryFile(file: any): any;
+}
+
+declare var EXIF : EXIFStatic;
+export = EXIF;

+ 1059 - 0
exif-js/exif.js

@@ -0,0 +1,1059 @@
+(function() {
+
+    var debug = false;
+
+    var root = this;
+
+    var EXIF = function(obj) {
+        if (obj instanceof EXIF) return obj;
+        if (!(this instanceof EXIF)) return new EXIF(obj);
+        this.EXIFwrapped = obj;
+    };
+
+    if (typeof exports !== 'undefined') {
+        if (typeof module !== 'undefined' && module.exports) {
+            exports = module.exports = EXIF;
+        }
+        exports.EXIF = EXIF;
+    } else {
+        root.EXIF = EXIF;
+    }
+
+    var ExifTags = EXIF.Tags = {
+
+        // version tags
+        0x9000 : "ExifVersion",             // EXIF version
+        0xA000 : "FlashpixVersion",         // Flashpix format version
+
+        // colorspace tags
+        0xA001 : "ColorSpace",              // Color space information tag
+
+        // image configuration
+        0xA002 : "PixelXDimension",         // Valid width of meaningful image
+        0xA003 : "PixelYDimension",         // Valid height of meaningful image
+        0x9101 : "ComponentsConfiguration", // Information about channels
+        0x9102 : "CompressedBitsPerPixel",  // Compressed bits per pixel
+
+        // user information
+        0x927C : "MakerNote",               // Any desired information written by the manufacturer
+        0x9286 : "UserComment",             // Comments by user
+
+        // related file
+        0xA004 : "RelatedSoundFile",        // Name of related sound file
+
+        // date and time
+        0x9003 : "DateTimeOriginal",        // Date and time when the original image was generated
+        0x9004 : "DateTimeDigitized",       // Date and time when the image was stored digitally
+        0x9290 : "SubsecTime",              // Fractions of seconds for DateTime
+        0x9291 : "SubsecTimeOriginal",      // Fractions of seconds for DateTimeOriginal
+        0x9292 : "SubsecTimeDigitized",     // Fractions of seconds for DateTimeDigitized
+
+        // picture-taking conditions
+        0x829A : "ExposureTime",            // Exposure time (in seconds)
+        0x829D : "FNumber",                 // F number
+        0x8822 : "ExposureProgram",         // Exposure program
+        0x8824 : "SpectralSensitivity",     // Spectral sensitivity
+        0x8827 : "ISOSpeedRatings",         // ISO speed rating
+        0x8828 : "OECF",                    // Optoelectric conversion factor
+        0x9201 : "ShutterSpeedValue",       // Shutter speed
+        0x9202 : "ApertureValue",           // Lens aperture
+        0x9203 : "BrightnessValue",         // Value of brightness
+        0x9204 : "ExposureBias",            // Exposure bias
+        0x9205 : "MaxApertureValue",        // Smallest F number of lens
+        0x9206 : "SubjectDistance",         // Distance to subject in meters
+        0x9207 : "MeteringMode",            // Metering mode
+        0x9208 : "LightSource",             // Kind of light source
+        0x9209 : "Flash",                   // Flash status
+        0x9214 : "SubjectArea",             // Location and area of main subject
+        0x920A : "FocalLength",             // Focal length of the lens in mm
+        0xA20B : "FlashEnergy",             // Strobe energy in BCPS
+        0xA20C : "SpatialFrequencyResponse",    //
+        0xA20E : "FocalPlaneXResolution",   // Number of pixels in width direction per FocalPlaneResolutionUnit
+        0xA20F : "FocalPlaneYResolution",   // Number of pixels in height direction per FocalPlaneResolutionUnit
+        0xA210 : "FocalPlaneResolutionUnit",    // Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution
+        0xA214 : "SubjectLocation",         // Location of subject in image
+        0xA215 : "ExposureIndex",           // Exposure index selected on camera
+        0xA217 : "SensingMethod",           // Image sensor type
+        0xA300 : "FileSource",              // Image source (3 == DSC)
+        0xA301 : "SceneType",               // Scene type (1 == directly photographed)
+        0xA302 : "CFAPattern",              // Color filter array geometric pattern
+        0xA401 : "CustomRendered",          // Special processing
+        0xA402 : "ExposureMode",            // Exposure mode
+        0xA403 : "WhiteBalance",            // 1 = auto white balance, 2 = manual
+        0xA404 : "DigitalZoomRation",       // Digital zoom ratio
+        0xA405 : "FocalLengthIn35mmFilm",   // Equivalent foacl length assuming 35mm film camera (in mm)
+        0xA406 : "SceneCaptureType",        // Type of scene
+        0xA407 : "GainControl",             // Degree of overall image gain adjustment
+        0xA408 : "Contrast",                // Direction of contrast processing applied by camera
+        0xA409 : "Saturation",              // Direction of saturation processing applied by camera
+        0xA40A : "Sharpness",               // Direction of sharpness processing applied by camera
+        0xA40B : "DeviceSettingDescription",    //
+        0xA40C : "SubjectDistanceRange",    // Distance to subject
+
+        // other tags
+        0xA005 : "InteroperabilityIFDPointer",
+        0xA420 : "ImageUniqueID"            // Identifier assigned uniquely to each image
+    };
+
+    var TiffTags = EXIF.TiffTags = {
+        0x0100 : "ImageWidth",
+        0x0101 : "ImageHeight",
+        0x8769 : "ExifIFDPointer",
+        0x8825 : "GPSInfoIFDPointer",
+        0xA005 : "InteroperabilityIFDPointer",
+        0x0102 : "BitsPerSample",
+        0x0103 : "Compression",
+        0x0106 : "PhotometricInterpretation",
+        0x0112 : "Orientation",
+        0x0115 : "SamplesPerPixel",
+        0x011C : "PlanarConfiguration",
+        0x0212 : "YCbCrSubSampling",
+        0x0213 : "YCbCrPositioning",
+        0x011A : "XResolution",
+        0x011B : "YResolution",
+        0x0128 : "ResolutionUnit",
+        0x0111 : "StripOffsets",
+        0x0116 : "RowsPerStrip",
+        0x0117 : "StripByteCounts",
+        0x0201 : "JPEGInterchangeFormat",
+        0x0202 : "JPEGInterchangeFormatLength",
+        0x012D : "TransferFunction",
+        0x013E : "WhitePoint",
+        0x013F : "PrimaryChromaticities",
+        0x0211 : "YCbCrCoefficients",
+        0x0214 : "ReferenceBlackWhite",
+        0x0132 : "DateTime",
+        0x010E : "ImageDescription",
+        0x010F : "Make",
+        0x0110 : "Model",
+        0x0131 : "Software",
+        0x013B : "Artist",
+        0x8298 : "Copyright"
+    };
+
+    var GPSTags = EXIF.GPSTags = {
+        0x0000 : "GPSVersionID",
+        0x0001 : "GPSLatitudeRef",
+        0x0002 : "GPSLatitude",
+        0x0003 : "GPSLongitudeRef",
+        0x0004 : "GPSLongitude",
+        0x0005 : "GPSAltitudeRef",
+        0x0006 : "GPSAltitude",
+        0x0007 : "GPSTimeStamp",
+        0x0008 : "GPSSatellites",
+        0x0009 : "GPSStatus",
+        0x000A : "GPSMeasureMode",
+        0x000B : "GPSDOP",
+        0x000C : "GPSSpeedRef",
+        0x000D : "GPSSpeed",
+        0x000E : "GPSTrackRef",
+        0x000F : "GPSTrack",
+        0x0010 : "GPSImgDirectionRef",
+        0x0011 : "GPSImgDirection",
+        0x0012 : "GPSMapDatum",
+        0x0013 : "GPSDestLatitudeRef",
+        0x0014 : "GPSDestLatitude",
+        0x0015 : "GPSDestLongitudeRef",
+        0x0016 : "GPSDestLongitude",
+        0x0017 : "GPSDestBearingRef",
+        0x0018 : "GPSDestBearing",
+        0x0019 : "GPSDestDistanceRef",
+        0x001A : "GPSDestDistance",
+        0x001B : "GPSProcessingMethod",
+        0x001C : "GPSAreaInformation",
+        0x001D : "GPSDateStamp",
+        0x001E : "GPSDifferential"
+    };
+
+     // EXIF 2.3 Spec
+    var IFD1Tags = EXIF.IFD1Tags = {
+        0x0100: "ImageWidth",
+        0x0101: "ImageHeight",
+        0x0102: "BitsPerSample",
+        0x0103: "Compression",
+        0x0106: "PhotometricInterpretation",
+        0x0111: "StripOffsets",
+        0x0112: "Orientation",
+        0x0115: "SamplesPerPixel",
+        0x0116: "RowsPerStrip",
+        0x0117: "StripByteCounts",
+        0x011A: "XResolution",
+        0x011B: "YResolution",
+        0x011C: "PlanarConfiguration",
+        0x0128: "ResolutionUnit",
+        0x0201: "JpegIFOffset",    // When image format is JPEG, this value show offset to JPEG data stored.(aka "ThumbnailOffset" or "JPEGInterchangeFormat")
+        0x0202: "JpegIFByteCount", // When image format is JPEG, this value shows data size of JPEG image (aka "ThumbnailLength" or "JPEGInterchangeFormatLength")
+        0x0211: "YCbCrCoefficients",
+        0x0212: "YCbCrSubSampling",
+        0x0213: "YCbCrPositioning",
+        0x0214: "ReferenceBlackWhite"
+    };
+
+    var StringValues = EXIF.StringValues = {
+        ExposureProgram : {
+            0 : "Not defined",
+            1 : "Manual",
+            2 : "Normal program",
+            3 : "Aperture priority",
+            4 : "Shutter priority",
+            5 : "Creative program",
+            6 : "Action program",
+            7 : "Portrait mode",
+            8 : "Landscape mode"
+        },
+        MeteringMode : {
+            0 : "Unknown",
+            1 : "Average",
+            2 : "CenterWeightedAverage",
+            3 : "Spot",
+            4 : "MultiSpot",
+            5 : "Pattern",
+            6 : "Partial",
+            255 : "Other"
+        },
+        LightSource : {
+            0 : "Unknown",
+            1 : "Daylight",
+            2 : "Fluorescent",
+            3 : "Tungsten (incandescent light)",
+            4 : "Flash",
+            9 : "Fine weather",
+            10 : "Cloudy weather",
+            11 : "Shade",
+            12 : "Daylight fluorescent (D 5700 - 7100K)",
+            13 : "Day white fluorescent (N 4600 - 5400K)",
+            14 : "Cool white fluorescent (W 3900 - 4500K)",
+            15 : "White fluorescent (WW 3200 - 3700K)",
+            17 : "Standard light A",
+            18 : "Standard light B",
+            19 : "Standard light C",
+            20 : "D55",
+            21 : "D65",
+            22 : "D75",
+            23 : "D50",
+            24 : "ISO studio tungsten",
+            255 : "Other"
+        },
+        Flash : {
+            0x0000 : "Flash did not fire",
+            0x0001 : "Flash fired",
+            0x0005 : "Strobe return light not detected",
+            0x0007 : "Strobe return light detected",
+            0x0009 : "Flash fired, compulsory flash mode",
+            0x000D : "Flash fired, compulsory flash mode, return light not detected",
+            0x000F : "Flash fired, compulsory flash mode, return light detected",
+            0x0010 : "Flash did not fire, compulsory flash mode",
+            0x0018 : "Flash did not fire, auto mode",
+            0x0019 : "Flash fired, auto mode",
+            0x001D : "Flash fired, auto mode, return light not detected",
+            0x001F : "Flash fired, auto mode, return light detected",
+            0x0020 : "No flash function",
+            0x0041 : "Flash fired, red-eye reduction mode",
+            0x0045 : "Flash fired, red-eye reduction mode, return light not detected",
+            0x0047 : "Flash fired, red-eye reduction mode, return light detected",
+            0x0049 : "Flash fired, compulsory flash mode, red-eye reduction mode",
+            0x004D : "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",
+            0x004F : "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",
+            0x0059 : "Flash fired, auto mode, red-eye reduction mode",
+            0x005D : "Flash fired, auto mode, return light not detected, red-eye reduction mode",
+            0x005F : "Flash fired, auto mode, return light detected, red-eye reduction mode"
+        },
+        SensingMethod : {
+            1 : "Not defined",
+            2 : "One-chip color area sensor",
+            3 : "Two-chip color area sensor",
+            4 : "Three-chip color area sensor",
+            5 : "Color sequential area sensor",
+            7 : "Trilinear sensor",
+            8 : "Color sequential linear sensor"
+        },
+        SceneCaptureType : {
+            0 : "Standard",
+            1 : "Landscape",
+            2 : "Portrait",
+            3 : "Night scene"
+        },
+        SceneType : {
+            1 : "Directly photographed"
+        },
+        CustomRendered : {
+            0 : "Normal process",
+            1 : "Custom process"
+        },
+        WhiteBalance : {
+            0 : "Auto white balance",
+            1 : "Manual white balance"
+        },
+        GainControl : {
+            0 : "None",
+            1 : "Low gain up",
+            2 : "High gain up",
+            3 : "Low gain down",
+            4 : "High gain down"
+        },
+        Contrast : {
+            0 : "Normal",
+            1 : "Soft",
+            2 : "Hard"
+        },
+        Saturation : {
+            0 : "Normal",
+            1 : "Low saturation",
+            2 : "High saturation"
+        },
+        Sharpness : {
+            0 : "Normal",
+            1 : "Soft",
+            2 : "Hard"
+        },
+        SubjectDistanceRange : {
+            0 : "Unknown",
+            1 : "Macro",
+            2 : "Close view",
+            3 : "Distant view"
+        },
+        FileSource : {
+            3 : "DSC"
+        },
+
+        Components : {
+            0 : "",
+            1 : "Y",
+            2 : "Cb",
+            3 : "Cr",
+            4 : "R",
+            5 : "G",
+            6 : "B"
+        }
+    };
+
+    function addEvent(element, event, handler) {
+        if (element.addEventListener) {
+            element.addEventListener(event, handler, false);
+        } else if (element.attachEvent) {
+            element.attachEvent("on" + event, handler);
+        }
+    }
+
+    function imageHasData(img) {
+        return !!(img.exifdata);
+    }
+
+
+    function base64ToArrayBuffer(base64, contentType) {
+        contentType = contentType || base64.match(/^data\:([^\;]+)\;base64,/mi)[1] || ''; // e.g. 'data:image/jpeg;base64,...' => 'image/jpeg'
+        base64 = base64.replace(/^data\:([^\;]+)\;base64,/gmi, '');
+        var binary = atob(base64);
+        var len = binary.length;
+        var buffer = new ArrayBuffer(len);
+        var view = new Uint8Array(buffer);
+        for (var i = 0; i < len; i++) {
+            view[i] = binary.charCodeAt(i);
+        }
+        return buffer;
+    }
+
+    function objectURLToBlob(url, callback) {
+        var http = new XMLHttpRequest();
+        http.open("GET", url, true);
+        http.responseType = "blob";
+        http.onload = function(e) {
+            if (this.status == 200 || this.status === 0) {
+                callback(this.response);
+            }
+        };
+        http.send();
+    }
+
+    function getImageData(img, callback) {
+        function handleBinaryFile(binFile) {
+            var data = findEXIFinJPEG(binFile);
+            img.exifdata = data || {};
+            var iptcdata = findIPTCinJPEG(binFile);
+            img.iptcdata = iptcdata || {};
+            if (EXIF.isXmpEnabled) {
+               var xmpdata= findXMPinJPEG(binFile);
+               img.xmpdata = xmpdata || {};               
+            }
+            if (callback) {
+                callback.call(img);
+            }
+        }
+
+        if (img.src) {
+            if (/^data\:/i.test(img.src)) { // Data URI
+                var arrayBuffer = base64ToArrayBuffer(img.src);
+                handleBinaryFile(arrayBuffer);
+
+            } else if (/^blob\:/i.test(img.src)) { // Object URL
+                var fileReader = new FileReader();
+                fileReader.onload = function(e) {
+                    handleBinaryFile(e.target.result);
+                };
+                objectURLToBlob(img.src, function (blob) {
+                    fileReader.readAsArrayBuffer(blob);
+                });
+            } else {
+                var http = new XMLHttpRequest();
+                http.onload = function() {
+                    if (this.status == 200 || this.status === 0) {
+                        handleBinaryFile(http.response);
+                    } else {
+                        throw "Could not load image";
+                    }
+                    http = null;
+                };
+                http.open("GET", img.src, true);
+                http.responseType = "arraybuffer";
+                http.send(null);
+            }
+        } else if (self.FileReader && (img instanceof self.Blob || img instanceof self.File)) {
+            var fileReader = new FileReader();
+            fileReader.onload = function(e) {
+                if (debug) console.log("Got file of length " + e.target.result.byteLength);
+                handleBinaryFile(e.target.result);
+            };
+
+            fileReader.readAsArrayBuffer(img);
+        }
+    }
+
+    function findEXIFinJPEG(file) {
+        var dataView = new DataView(file);
+
+        if (debug) console.log("Got file of length " + file.byteLength);
+        if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
+            if (debug) console.log("Not a valid JPEG");
+            return false; // not a valid jpeg
+        }
+
+        var offset = 2,
+            length = file.byteLength,
+            marker;
+
+        while (offset < length) {
+            if (dataView.getUint8(offset) != 0xFF) {
+                if (debug) console.log("Not a valid marker at offset " + offset + ", found: " + dataView.getUint8(offset));
+                return false; // not a valid marker, something is wrong
+            }
+
+            marker = dataView.getUint8(offset + 1);
+            if (debug) console.log(marker);
+
+            // we could implement handling for other markers here,
+            // but we're only looking for 0xFFE1 for EXIF data
+
+            if (marker == 225) {
+                if (debug) console.log("Found 0xFFE1 marker");
+
+                return readEXIFData(dataView, offset + 4, dataView.getUint16(offset + 2) - 2);
+
+                // offset += 2 + file.getShortAt(offset+2, true);
+
+            } else {
+                offset += 2 + dataView.getUint16(offset+2);
+            }
+
+        }
+
+    }
+
+    function findIPTCinJPEG(file) {
+        var dataView = new DataView(file);
+
+        if (debug) console.log("Got file of length " + file.byteLength);
+        if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
+            if (debug) console.log("Not a valid JPEG");
+            return false; // not a valid jpeg
+        }
+
+        var offset = 2,
+            length = file.byteLength;
+
+
+        var isFieldSegmentStart = function(dataView, offset){
+            return (
+                dataView.getUint8(offset) === 0x38 &&
+                dataView.getUint8(offset+1) === 0x42 &&
+                dataView.getUint8(offset+2) === 0x49 &&
+                dataView.getUint8(offset+3) === 0x4D &&
+                dataView.getUint8(offset+4) === 0x04 &&
+                dataView.getUint8(offset+5) === 0x04
+            );
+        };
+
+        while (offset < length) {
+
+            if ( isFieldSegmentStart(dataView, offset )){
+
+                // Get the length of the name header (which is padded to an even number of bytes)
+                var nameHeaderLength = dataView.getUint8(offset+7);
+                if(nameHeaderLength % 2 !== 0) nameHeaderLength += 1;
+                // Check for pre photoshop 6 format
+                if(nameHeaderLength === 0) {
+                    // Always 4
+                    nameHeaderLength = 4;
+                }
+
+                var startOffset = offset + 8 + nameHeaderLength;
+                var sectionLength = dataView.getUint16(offset + 6 + nameHeaderLength);
+
+                return readIPTCData(file, startOffset, sectionLength);
+
+                break;
+
+            }
+
+
+            // Not the marker, continue searching
+            offset++;
+
+        }
+
+    }
+    var IptcFieldMap = {
+        0x78 : 'caption',
+        0x6E : 'credit',
+        0x19 : 'keywords',
+        0x37 : 'dateCreated',
+        0x50 : 'byline',
+        0x55 : 'bylineTitle',
+        0x7A : 'captionWriter',
+        0x69 : 'headline',
+        0x74 : 'copyright',
+        0x0F : 'category'
+    };
+    function readIPTCData(file, startOffset, sectionLength){
+        var dataView = new DataView(file);
+        var data = {};
+        var fieldValue, fieldName, dataSize, segmentType, segmentSize;
+        var segmentStartPos = startOffset;
+        while(segmentStartPos < startOffset+sectionLength) {
+            if(dataView.getUint8(segmentStartPos) === 0x1C && dataView.getUint8(segmentStartPos+1) === 0x02){
+                segmentType = dataView.getUint8(segmentStartPos+2);
+                if(segmentType in IptcFieldMap) {
+                    dataSize = dataView.getInt16(segmentStartPos+3);
+                    segmentSize = dataSize + 5;
+                    fieldName = IptcFieldMap[segmentType];
+                    fieldValue = getStringFromDB(dataView, segmentStartPos+5, dataSize);
+                    // Check if we already stored a value with this name
+                    if(data.hasOwnProperty(fieldName)) {
+                        // Value already stored with this name, create multivalue field
+                        if(data[fieldName] instanceof Array) {
+                            data[fieldName].push(fieldValue);
+                        }
+                        else {
+                            data[fieldName] = [data[fieldName], fieldValue];
+                        }
+                    }
+                    else {
+                        data[fieldName] = fieldValue;
+                    }
+                }
+
+            }
+            segmentStartPos++;
+        }
+        return data;
+    }
+
+
+
+    function readTags(file, tiffStart, dirStart, strings, bigEnd) {
+        var entries = file.getUint16(dirStart, !bigEnd),
+            tags = {},
+            entryOffset, tag,
+            i;
+
+        for (i=0;i<entries;i++) {
+            entryOffset = dirStart + i*12 + 2;
+            tag = strings[file.getUint16(entryOffset, !bigEnd)];
+            if (!tag && debug) console.log("Unknown tag: " + file.getUint16(entryOffset, !bigEnd));
+            tags[tag] = readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd);
+        }
+        return tags;
+    }
+
+
+    function readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd) {
+        var type = file.getUint16(entryOffset+2, !bigEnd),
+            numValues = file.getUint32(entryOffset+4, !bigEnd),
+            valueOffset = file.getUint32(entryOffset+8, !bigEnd) + tiffStart,
+            offset,
+            vals, val, n,
+            numerator, denominator;
+
+        switch (type) {
+            case 1: // byte, 8-bit unsigned int
+            case 7: // undefined, 8-bit byte, value depending on field
+                if (numValues == 1) {
+                    return file.getUint8(entryOffset + 8, !bigEnd);
+                } else {
+                    offset = numValues > 4 ? valueOffset : (entryOffset + 8);
+                    vals = [];
+                    for (n=0;n<numValues;n++) {
+                        vals[n] = file.getUint8(offset + n);
+                    }
+                    return vals;
+                }
+
+            case 2: // ascii, 8-bit byte
+                offset = numValues > 4 ? valueOffset : (entryOffset + 8);
+                return getStringFromDB(file, offset, numValues-1);
+
+            case 3: // short, 16 bit int
+                if (numValues == 1) {
+                    return file.getUint16(entryOffset + 8, !bigEnd);
+                } else {
+                    offset = numValues > 2 ? valueOffset : (entryOffset + 8);
+                    vals = [];
+                    for (n=0;n<numValues;n++) {
+                        vals[n] = file.getUint16(offset + 2*n, !bigEnd);
+                    }
+                    return vals;
+                }
+
+            case 4: // long, 32 bit int
+                if (numValues == 1) {
+                    return file.getUint32(entryOffset + 8, !bigEnd);
+                } else {
+                    vals = [];
+                    for (n=0;n<numValues;n++) {
+                        vals[n] = file.getUint32(valueOffset + 4*n, !bigEnd);
+                    }
+                    return vals;
+                }
+
+            case 5:    // rational = two long values, first is numerator, second is denominator
+                if (numValues == 1) {
+                    numerator = file.getUint32(valueOffset, !bigEnd);
+                    denominator = file.getUint32(valueOffset+4, !bigEnd);
+                    val = new Number(numerator / denominator);
+                    val.numerator = numerator;
+                    val.denominator = denominator;
+                    return val;
+                } else {
+                    vals = [];
+                    for (n=0;n<numValues;n++) {
+                        numerator = file.getUint32(valueOffset + 8*n, !bigEnd);
+                        denominator = file.getUint32(valueOffset+4 + 8*n, !bigEnd);
+                        vals[n] = new Number(numerator / denominator);
+                        vals[n].numerator = numerator;
+                        vals[n].denominator = denominator;
+                    }
+                    return vals;
+                }
+
+            case 9: // slong, 32 bit signed int
+                if (numValues == 1) {
+                    return file.getInt32(entryOffset + 8, !bigEnd);
+                } else {
+                    vals = [];
+                    for (n=0;n<numValues;n++) {
+                        vals[n] = file.getInt32(valueOffset + 4*n, !bigEnd);
+                    }
+                    return vals;
+                }
+
+            case 10: // signed rational, two slongs, first is numerator, second is denominator
+                if (numValues == 1) {
+                    return file.getInt32(valueOffset, !bigEnd) / file.getInt32(valueOffset+4, !bigEnd);
+                } else {
+                    vals = [];
+                    for (n=0;n<numValues;n++) {
+                        vals[n] = file.getInt32(valueOffset + 8*n, !bigEnd) / file.getInt32(valueOffset+4 + 8*n, !bigEnd);
+                    }
+                    return vals;
+                }
+        }
+    }
+
+    /**
+    * Given an IFD (Image File Directory) start offset
+    * returns an offset to next IFD or 0 if it's the last IFD.
+    */
+    function getNextIFDOffset(dataView, dirStart, bigEnd){
+        //the first 2bytes means the number of directory entries contains in this IFD
+        var entries = dataView.getUint16(dirStart, !bigEnd);
+
+        // After last directory entry, there is a 4bytes of data,
+        // it means an offset to next IFD.
+        // If its value is '0x00000000', it means this is the last IFD and there is no linked IFD.
+
+        return dataView.getUint32(dirStart + 2 + entries * 12, !bigEnd); // each entry is 12 bytes long
+    }
+
+    function readThumbnailImage(dataView, tiffStart, firstIFDOffset, bigEnd){
+        // get the IFD1 offset
+        var IFD1OffsetPointer = getNextIFDOffset(dataView, tiffStart+firstIFDOffset, bigEnd);
+
+        if (!IFD1OffsetPointer) {
+            // console.log('******** IFD1Offset is empty, image thumb not found ********');
+            return {};
+        }
+        else if (IFD1OffsetPointer > dataView.byteLength) { // this should not happen
+            // console.log('******** IFD1Offset is outside the bounds of the DataView ********');
+            return {};
+        }
+        // console.log('*******  thumbnail IFD offset (IFD1) is: %s', IFD1OffsetPointer);
+
+        var thumbTags = readTags(dataView, tiffStart, tiffStart + IFD1OffsetPointer, IFD1Tags, bigEnd)
+
+        // EXIF 2.3 specification for JPEG format thumbnail
+
+        // If the value of Compression(0x0103) Tag in IFD1 is '6', thumbnail image format is JPEG.
+        // Most of Exif image uses JPEG format for thumbnail. In that case, you can get offset of thumbnail
+        // by JpegIFOffset(0x0201) Tag in IFD1, size of thumbnail by JpegIFByteCount(0x0202) Tag.
+        // Data format is ordinary JPEG format, starts from 0xFFD8 and ends by 0xFFD9. It seems that
+        // JPEG format and 160x120pixels of size are recommended thumbnail format for Exif2.1 or later.
+
+        if (thumbTags['Compression']) {
+            // console.log('Thumbnail image found!');
+
+            switch (thumbTags['Compression']) {
+                case 6:
+                    // console.log('Thumbnail image format is JPEG');
+                    if (thumbTags.JpegIFOffset && thumbTags.JpegIFByteCount) {
+                    // extract the thumbnail
+                        var tOffset = tiffStart + thumbTags.JpegIFOffset;
+                        var tLength = thumbTags.JpegIFByteCount;
+                        thumbTags['blob'] = new Blob([new Uint8Array(dataView.buffer, tOffset, tLength)], {
+                            type: 'image/jpeg'
+                        });
+                    }
+                break;
+
+            case 1:
+                console.log("Thumbnail image format is TIFF, which is not implemented.");
+                break;
+            default:
+                console.log("Unknown thumbnail image format '%s'", thumbTags['Compression']);
+            }
+        }
+        else if (thumbTags['PhotometricInterpretation'] == 2) {
+            console.log("Thumbnail image format is RGB, which is not implemented.");
+        }
+        return thumbTags;
+    }
+
+    function getStringFromDB(buffer, start, length) {
+        var outstr = "";
+        for (n = start; n < start+length; n++) {
+            outstr += String.fromCharCode(buffer.getUint8(n));
+        }
+        return outstr;
+    }
+
+    function readEXIFData(file, start) {
+        if (getStringFromDB(file, start, 4) != "Exif") {
+            if (debug) console.log("Not valid EXIF data! " + getStringFromDB(file, start, 4));
+            return false;
+        }
+
+        var bigEnd,
+            tags, tag,
+            exifData, gpsData,
+            tiffOffset = start + 6;
+
+        // test for TIFF validity and endianness
+        if (file.getUint16(tiffOffset) == 0x4949) {
+            bigEnd = false;
+        } else if (file.getUint16(tiffOffset) == 0x4D4D) {
+            bigEnd = true;
+        } else {
+            if (debug) console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)");
+            return false;
+        }
+
+        if (file.getUint16(tiffOffset+2, !bigEnd) != 0x002A) {
+            if (debug) console.log("Not valid TIFF data! (no 0x002A)");
+            return false;
+        }
+
+        var firstIFDOffset = file.getUint32(tiffOffset+4, !bigEnd);
+
+        if (firstIFDOffset < 0x00000008) {
+            if (debug) console.log("Not valid TIFF data! (First offset less than 8)", file.getUint32(tiffOffset+4, !bigEnd));
+            return false;
+        }
+
+        tags = readTags(file, tiffOffset, tiffOffset + firstIFDOffset, TiffTags, bigEnd);
+
+        if (tags.ExifIFDPointer) {
+            exifData = readTags(file, tiffOffset, tiffOffset + tags.ExifIFDPointer, ExifTags, bigEnd);
+            for (tag in exifData) {
+                switch (tag) {
+                    case "LightSource" :
+                    case "Flash" :
+                    case "MeteringMode" :
+                    case "ExposureProgram" :
+                    case "SensingMethod" :
+                    case "SceneCaptureType" :
+                    case "SceneType" :
+                    case "CustomRendered" :
+                    case "WhiteBalance" :
+                    case "GainControl" :
+                    case "Contrast" :
+                    case "Saturation" :
+                    case "Sharpness" :
+                    case "SubjectDistanceRange" :
+                    case "FileSource" :
+                        exifData[tag] = StringValues[tag][exifData[tag]];
+                        break;
+
+                    case "ExifVersion" :
+                    case "FlashpixVersion" :
+                        exifData[tag] = String.fromCharCode(exifData[tag][0], exifData[tag][1], exifData[tag][2], exifData[tag][3]);
+                        break;
+
+                    case "ComponentsConfiguration" :
+                        exifData[tag] =
+                            StringValues.Components[exifData[tag][0]] +
+                            StringValues.Components[exifData[tag][1]] +
+                            StringValues.Components[exifData[tag][2]] +
+                            StringValues.Components[exifData[tag][3]];
+                        break;
+                }
+                tags[tag] = exifData[tag];
+            }
+        }
+
+        if (tags.GPSInfoIFDPointer) {
+            gpsData = readTags(file, tiffOffset, tiffOffset + tags.GPSInfoIFDPointer, GPSTags, bigEnd);
+            for (tag in gpsData) {
+                switch (tag) {
+                    case "GPSVersionID" :
+                        gpsData[tag] = gpsData[tag][0] +
+                            "." + gpsData[tag][1] +
+                            "." + gpsData[tag][2] +
+                            "." + gpsData[tag][3];
+                        break;
+                }
+                tags[tag] = gpsData[tag];
+            }
+        }
+
+        // extract thumbnail
+        tags['thumbnail'] = readThumbnailImage(file, tiffOffset, firstIFDOffset, bigEnd);
+
+        return tags;
+    }
+
+   function findXMPinJPEG(file) {
+
+        if (!('DOMParser' in self)) {
+            // console.warn('XML parsing not supported without DOMParser');
+            return;
+        }
+        var dataView = new DataView(file);
+
+        if (debug) console.log("Got file of length " + file.byteLength);
+        if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
+           if (debug) console.log("Not a valid JPEG");
+           return false; // not a valid jpeg
+        }
+
+        var offset = 2,
+            length = file.byteLength,
+            dom = new DOMParser();
+
+        while (offset < (length-4)) {
+            if (getStringFromDB(dataView, offset, 4) == "http") {
+                var startOffset = offset - 1;
+                var sectionLength = dataView.getUint16(offset - 2) - 1;
+                var xmpString = getStringFromDB(dataView, startOffset, sectionLength)
+                var xmpEndIndex = xmpString.indexOf('xmpmeta>') + 8;
+                xmpString = xmpString.substring( xmpString.indexOf( '<x:xmpmeta' ), xmpEndIndex );
+
+                var indexOfXmp = xmpString.indexOf('x:xmpmeta') + 10
+                //Many custom written programs embed xmp/xml without any namespace. Following are some of them.
+                //Without these namespaces, XML is thought to be invalid by parsers
+                xmpString = xmpString.slice(0, indexOfXmp)
+                            + 'xmlns:Iptc4xmpCore="http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/" '
+                            + 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '
+                            + 'xmlns:tiff="http://ns.adobe.com/tiff/1.0/" '
+                            + 'xmlns:plus="http://schemas.android.com/apk/lib/com.google.android.gms.plus" '
+                            + 'xmlns:ext="http://www.gettyimages.com/xsltExtension/1.0" '
+                            + 'xmlns:exif="http://ns.adobe.com/exif/1.0/" '
+                            + 'xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" '
+                            + 'xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" '
+                            + 'xmlns:crs="http://ns.adobe.com/camera-raw-settings/1.0/" '
+                            + 'xmlns:xapGImg="http://ns.adobe.com/xap/1.0/g/img/" '
+                            + 'xmlns:Iptc4xmpExt="http://iptc.org/std/Iptc4xmpExt/2008-02-29/" '
+                            + xmpString.slice(indexOfXmp)
+
+                var domDocument = dom.parseFromString( xmpString, 'text/xml' );
+                return xml2Object(domDocument);
+            } else{
+             offset++;
+            }
+        }
+    }
+
+    function xml2json(xml) {
+        var json = {};
+      
+        if (xml.nodeType == 1) { // element node
+          if (xml.attributes.length > 0) {
+            json['@attributes'] = {};
+            for (var j = 0; j < xml.attributes.length; j++) {
+              var attribute = xml.attributes.item(j);
+              json['@attributes'][attribute.nodeName] = attribute.nodeValue;
+            }
+          }
+        } else if (xml.nodeType == 3) { // text node
+          return xml.nodeValue;
+        }
+      
+        // deal with children
+        if (xml.hasChildNodes()) {
+          for(var i = 0; i < xml.childNodes.length; i++) {
+            var child = xml.childNodes.item(i);
+            var nodeName = child.nodeName;
+            if (json[nodeName] == null) {
+              json[nodeName] = xml2json(child);
+            } else {
+              if (json[nodeName].push == null) {
+                var old = json[nodeName];
+                json[nodeName] = [];
+                json[nodeName].push(old);
+              }
+              json[nodeName].push(xml2json(child));
+            }
+          }
+        }
+        
+        return json;
+    }
+
+    function xml2Object(xml) {
+        try {
+            var obj = {};
+            if (xml.children.length > 0) {
+              for (var i = 0; i < xml.children.length; i++) {
+                var item = xml.children.item(i);
+                var attributes = item.attributes;
+                for(var idx in attributes) {
+                    var itemAtt = attributes[idx];
+                    var dataKey = itemAtt.nodeName;
+                    var dataValue = itemAtt.nodeValue;
+
+                    if(dataKey !== undefined) {
+                        obj[dataKey] = dataValue;
+                    }
+                }
+                var nodeName = item.nodeName;
+
+                if (typeof (obj[nodeName]) == "undefined") {
+                  obj[nodeName] = xml2json(item);
+                } else {
+                  if (typeof (obj[nodeName].push) == "undefined") {
+                    var old = obj[nodeName];
+
+                    obj[nodeName] = [];
+                    obj[nodeName].push(old);
+                  }
+                  obj[nodeName].push(xml2json(item));
+                }
+              }
+            } else {
+              obj = xml.textContent;
+            }
+            return obj;
+          } catch (e) {
+              console.log(e.message);
+          }
+    }
+
+    EXIF.enableXmp = function() {
+        EXIF.isXmpEnabled = true;
+    }
+
+    EXIF.disableXmp = function() {
+        EXIF.isXmpEnabled = false;
+    }
+
+    EXIF.getData = function(img, callback) {
+        if (((self.Image && img instanceof self.Image)
+            || (self.HTMLImageElement && img instanceof self.HTMLImageElement))
+            && !img.complete)
+            return false;
+
+        if (!imageHasData(img)) {
+            getImageData(img, callback);
+        } else {
+            if (callback) {
+                callback.call(img);
+            }
+        }
+        return true;
+    }
+
+    EXIF.getTag = function(img, tag) {
+        if (!imageHasData(img)) return;
+        return img.exifdata[tag];
+    }
+    
+    EXIF.getIptcTag = function(img, tag) {
+        if (!imageHasData(img)) return;
+        return img.iptcdata[tag];
+    }
+
+    EXIF.getAllTags = function(img) {
+        if (!imageHasData(img)) return {};
+        var a,
+            data = img.exifdata,
+            tags = {};
+        for (a in data) {
+            if (data.hasOwnProperty(a)) {
+                tags[a] = data[a];
+            }
+        }
+        return tags;
+    }
+    
+    EXIF.getAllIptcTags = function(img) {
+        if (!imageHasData(img)) return {};
+        var a,
+            data = img.iptcdata,
+            tags = {};
+        for (a in data) {
+            if (data.hasOwnProperty(a)) {
+                tags[a] = data[a];
+            }
+        }
+        return tags;
+    }
+
+    EXIF.pretty = function(img) {
+        if (!imageHasData(img)) return "";
+        var a,
+            data = img.exifdata,
+            strPretty = "";
+        for (a in data) {
+            if (data.hasOwnProperty(a)) {
+                if (typeof data[a] == "object") {
+                    if (data[a] instanceof Number) {
+                        strPretty += a + " : " + data[a] + " [" + data[a].numerator + "/" + data[a].denominator + "]\r\n";
+                    } else {
+                        strPretty += a + " : [" + data[a].length + " values]\r\n";
+                    }
+                } else {
+                    strPretty += a + " : " + data[a] + "\r\n";
+                }
+            }
+        }
+        return strPretty;
+    }
+
+    EXIF.readFromBinaryFile = function(file) {
+        return findEXIFinJPEG(file);
+    }
+
+    if (typeof define === 'function' && define.amd) {
+        define('exif-js', [], function() {
+            return EXIF;
+        });
+    }
+}.call(this));
+

Разлика између датотеке није приказан због своје велике величине
+ 72 - 0
exif-js/index.html


+ 67 - 0
exif-js/package.json

@@ -0,0 +1,67 @@
+{
+  "_from": "exif-js@^2.3.0",
+  "_id": "exif-js@2.3.0",
+  "_inBundle": false,
+  "_integrity": "sha1-nRCBm/Vx+HOBPnZAJBJVq5zhqBQ=",
+  "_location": "/exif-js",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "range",
+    "registry": true,
+    "raw": "exif-js@^2.3.0",
+    "name": "exif-js",
+    "escapedName": "exif-js",
+    "rawSpec": "^2.3.0",
+    "saveSpec": null,
+    "fetchSpec": "^2.3.0"
+  },
+  "_requiredBy": [
+    "/qiniu-js"
+  ],
+  "_resolved": "https://registry.npmjs.org/exif-js/-/exif-js-2.3.0.tgz",
+  "_shasum": "9d10819bf571f873813e7640241255ab9ce1a814",
+  "_spec": "exif-js@^2.3.0",
+  "_where": "D:\\工作\\项目2\\test\\node_modules\\qiniu-js",
+  "author": {
+    "name": "Jacob Seidelin"
+  },
+  "bugs": {
+    "url": "https://github.com/exif-js/exif-js/issues"
+  },
+  "bundleDependencies": false,
+  "deprecated": false,
+  "description": "JavaScript library for reading EXIF image metadata",
+  "devDependencies": {
+    "bower": "^1.4.2",
+    "mversion": "^1.10.0"
+  },
+  "directories": {
+    "example": "example"
+  },
+  "homepage": "https://github.com/exif-js/exif-js",
+  "ignore": [
+    "**/.*",
+    "node_modules",
+    "bower_components",
+    "test",
+    "tests",
+    "spec",
+    "example"
+  ],
+  "keywords": [
+    "exif"
+  ],
+  "license": "MIT",
+  "main": "exif.js",
+  "maintainers": [
+    {
+      "name": "Roald de Vries"
+    }
+  ],
+  "name": "exif-js",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/exif-js/exif-js.git"
+  },
+  "version": "2.3.0"
+}

BIN
exif-js/spec/Exif2-2.pdf


+ 1 - 549
package-lock.json

@@ -1,551 +1,3 @@
 {
-  "requires": true,
-  "lockfileVersion": 1,
-  "dependencies": {
-    "aes-decrypter": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npm.taobao.org/aes-decrypter/download/aes-decrypter-1.0.3.tgz",
-      "integrity": "sha1-nAa4pUNaWtCduTP4oBSvzxhMw04=",
-      "requires": {
-        "pkcs7": "^0.2.3"
-      }
-    },
-    "ansi-regex": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
-      "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
-    },
-    "ansi-styles": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-      "requires": {
-        "color-convert": "^1.9.0"
-      }
-    },
-    "babel-runtime": {
-      "version": "6.26.0",
-      "resolved": "https://registry.npm.taobao.org/babel-runtime/download/babel-runtime-6.26.0.tgz",
-      "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
-      "requires": {
-        "core-js": "^2.4.0",
-        "regenerator-runtime": "^0.11.0"
-      }
-    },
-    "base64-arraybuffer": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz",
-      "integrity": "sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ=="
-    },
-    "base64-js": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
-      "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
-    },
-    "buffer": {
-      "version": "5.6.0",
-      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
-      "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
-      "requires": {
-        "base64-js": "^1.0.2",
-        "ieee754": "^1.1.4"
-      }
-    },
-    "buffer-alloc": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
-      "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
-      "requires": {
-        "buffer-alloc-unsafe": "^1.1.0",
-        "buffer-fill": "^1.0.0"
-      }
-    },
-    "buffer-alloc-unsafe": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
-      "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg=="
-    },
-    "buffer-fill": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
-      "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw="
-    },
-    "buffer-from": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
-      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
-    },
-    "camelcase": {
-      "version": "5.3.1",
-      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
-      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
-    },
-    "cliui": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
-      "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
-      "requires": {
-        "string-width": "^3.1.0",
-        "strip-ansi": "^5.2.0",
-        "wrap-ansi": "^5.1.0"
-      }
-    },
-    "color-convert": {
-      "version": "1.9.3",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-      "requires": {
-        "color-name": "1.1.3"
-      }
-    },
-    "color-name": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
-    },
-    "core-js": {
-      "version": "2.6.11",
-      "resolved": "https://registry.npm.taobao.org/core-js/download/core-js-2.6.11.tgz?cache=0&sync_timestamp=1592843203000&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcore-js%2Fdownload%2Fcore-js-2.6.11.tgz",
-      "integrity": "sha1-OIMUafmSK97Y7iHJ3EaYXgOZMIw="
-    },
-    "css-line-break": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-1.1.1.tgz",
-      "integrity": "sha512-1feNVaM4Fyzdj4mKPIQNL2n70MmuYzAXZ1aytlROFX1JsOo070OsugwGjj7nl6jnDJWHDM8zRZswkmeYVWZJQA==",
-      "requires": {
-        "base64-arraybuffer": "^0.2.0"
-      }
-    },
-    "decamelize": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
-      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
-    },
-    "dijkstrajs": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.1.tgz",
-      "integrity": "sha1-082BIh4+pAdCz83lVtTpnpjdxxs="
-    },
-    "dom-walk": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npm.taobao.org/dom-walk/download/dom-walk-0.1.2.tgz",
-      "integrity": "sha1-DFSL7wSPTR8qlySQAiNgYNqj/YQ="
-    },
-    "emoji-regex": {
-      "version": "7.0.3",
-      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
-      "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
-    },
-    "es5-shim": {
-      "version": "4.5.14",
-      "resolved": "https://registry.npm.taobao.org/es5-shim/download/es5-shim-4.5.14.tgz",
-      "integrity": "sha1-kACeEBnQ6jJ0R8tSPer/j+RWl+8="
-    },
-    "find-up": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
-      "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
-      "requires": {
-        "locate-path": "^3.0.0"
-      }
-    },
-    "get-caller-file": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
-      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
-    },
-    "global": {
-      "version": "4.3.2",
-      "resolved": "https://registry.npm.taobao.org/global/download/global-4.3.2.tgz",
-      "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=",
-      "requires": {
-        "min-document": "^2.19.0",
-        "process": "~0.5.1"
-      }
-    },
-    "html2canvas": {
-      "version": "1.0.0-rc.5",
-      "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.0.0-rc.5.tgz",
-      "integrity": "sha512-DtNqPxJNXPoTajs+lVQzGS1SULRI4GQaROeU5R41xH8acffHukxRh/NBVcTBsfCkJSkLq91rih5TpbEwUP9yWA==",
-      "requires": {
-        "css-line-break": "1.1.1"
-      }
-    },
-    "ieee754": {
-      "version": "1.1.13",
-      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
-      "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
-    },
-    "individual": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npm.taobao.org/individual/download/individual-2.0.0.tgz",
-      "integrity": "sha1-gzsJfa0jKU52EXqY+zjg2a1hu5c="
-    },
-    "is-fullwidth-code-point": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
-    },
-    "is-function": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npm.taobao.org/is-function/download/is-function-1.0.2.tgz",
-      "integrity": "sha1-Twl/MKv2762smDOxfKXcA/gUTgg="
-    },
-    "isarray": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
-      "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
-    },
-    "jweixin-module": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/jweixin-module/-/jweixin-module-1.6.0.tgz",
-      "integrity": "sha512-dGk9cf+ipipHmtzYmKZs5B2toX+p4hLyllGLF6xuC8t+B05oYxd8fYoaRz0T30U2n3RUv8a4iwvjhA+OcYz52w=="
-    },
-    "locate-path": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
-      "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
-      "requires": {
-        "p-locate": "^3.0.0",
-        "path-exists": "^3.0.0"
-      }
-    },
-    "m3u8-parser": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npm.taobao.org/m3u8-parser/download/m3u8-parser-2.1.0.tgz",
-      "integrity": "sha1-yBcDKewc1RXQ1Yu4t2LamJbLA2g="
-    },
-    "min-document": {
-      "version": "2.19.0",
-      "resolved": "https://registry.npm.taobao.org/min-document/download/min-document-2.19.0.tgz",
-      "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=",
-      "requires": {
-        "dom-walk": "^0.1.0"
-      }
-    },
-    "mux.js": {
-      "version": "4.3.2",
-      "resolved": "https://registry.npm.taobao.org/mux.js/download/mux.js-4.3.2.tgz",
-      "integrity": "sha1-V21TffA33F7DXsExa5SNgV01whA="
-    },
-    "object-assign": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fobject-assign%2Fdownload%2Fobject-assign-4.1.1.tgz",
-      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
-    },
-    "p-limit": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
-      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
-      "requires": {
-        "p-try": "^2.0.0"
-      }
-    },
-    "p-locate": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
-      "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
-      "requires": {
-        "p-limit": "^2.0.0"
-      }
-    },
-    "p-try": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
-      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
-    },
-    "parse-headers": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npm.taobao.org/parse-headers/download/parse-headers-2.0.3.tgz",
-      "integrity": "sha1-Xo51Ejg9FAugLwx6qfSbQ5nJJRU="
-    },
-    "path-exists": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-      "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
-    },
-    "pkcs7": {
-      "version": "0.2.3",
-      "resolved": "https://registry.npm.taobao.org/pkcs7/download/pkcs7-0.2.3.tgz",
-      "integrity": "sha1-ItYGZtAQZcXyRDkJjkpIMEUic74="
-    },
-    "pngjs": {
-      "version": "3.4.0",
-      "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
-      "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w=="
-    },
-    "process": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npm.taobao.org/process/download/process-0.5.2.tgz",
-      "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8="
-    },
-    "qrcode": {
-      "version": "1.4.4",
-      "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.4.4.tgz",
-      "integrity": "sha512-oLzEC5+NKFou9P0bMj5+v6Z40evexeE29Z9cummZXZ9QXyMr3lphkURzxjXgPJC5azpxcshoDWV1xE46z+/c3Q==",
-      "requires": {
-        "buffer": "^5.4.3",
-        "buffer-alloc": "^1.2.0",
-        "buffer-from": "^1.1.1",
-        "dijkstrajs": "^1.0.1",
-        "isarray": "^2.0.1",
-        "pngjs": "^3.3.0",
-        "yargs": "^13.2.4"
-      }
-    },
-    "regenerator-runtime": {
-      "version": "0.11.1",
-      "resolved": "https://registry.npm.taobao.org/regenerator-runtime/download/regenerator-runtime-0.11.1.tgz",
-      "integrity": "sha1-vgWtf5v30i4Fb5cmzuUBf78Z4uk="
-    },
-    "require-directory": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
-    },
-    "require-main-filename": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
-      "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
-    },
-    "rust-result": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npm.taobao.org/rust-result/download/rust-result-1.0.0.tgz",
-      "integrity": "sha1-NMdbLm3Dn+WHXlveyFteD5FTb3I=",
-      "requires": {
-        "individual": "^2.0.0"
-      }
-    },
-    "safe-json-parse": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npm.taobao.org/safe-json-parse/download/safe-json-parse-4.0.0.tgz",
-      "integrity": "sha1-fA9XjPzNEtM6ccDgVBPi7KFx6qw=",
-      "requires": {
-        "rust-result": "^1.0.0"
-      }
-    },
-    "set-blocking": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
-    },
-    "string-width": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
-      "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
-      "requires": {
-        "emoji-regex": "^7.0.1",
-        "is-fullwidth-code-point": "^2.0.0",
-        "strip-ansi": "^5.1.0"
-      }
-    },
-    "strip-ansi": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
-      "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
-      "requires": {
-        "ansi-regex": "^4.1.0"
-      }
-    },
-    "tsml": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npm.taobao.org/tsml/download/tsml-1.0.1.tgz",
-      "integrity": "sha1-ifghi52eJX9H1/a1bQHFpNLGj8M="
-    },
-    "url-toolkit": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npm.taobao.org/url-toolkit/download/url-toolkit-2.2.0.tgz",
-      "integrity": "sha1-mle4nzFdS33DQOFQvPpUjd9fXOk="
-    },
-    "video.js": {
-      "version": "6.13.0",
-      "resolved": "https://registry.npm.taobao.org/video.js/download/video.js-6.13.0.tgz",
-      "integrity": "sha1-+Uh9RjJzQPpI7NUTcqKYHbts3kw=",
-      "requires": {
-        "babel-runtime": "^6.9.2",
-        "global": "4.3.2",
-        "safe-json-parse": "4.0.0",
-        "tsml": "1.0.1",
-        "videojs-font": "2.1.0",
-        "videojs-ie8": "1.1.2",
-        "videojs-vtt.js": "0.12.6",
-        "xhr": "2.4.0"
-      }
-    },
-    "videojs-contrib-hls": {
-      "version": "5.15.0",
-      "resolved": "https://registry.npm.taobao.org/videojs-contrib-hls/download/videojs-contrib-hls-5.15.0.tgz",
-      "integrity": "sha1-/klXNn5daLfSP3jtMuN6ndiSoKg=",
-      "requires": {
-        "aes-decrypter": "1.0.3",
-        "global": "^4.3.0",
-        "m3u8-parser": "2.1.0",
-        "mux.js": "4.3.2",
-        "url-toolkit": "^2.1.3",
-        "video.js": "^5.19.1 || ^6.2.0",
-        "videojs-contrib-media-sources": "4.7.2",
-        "webwackify": "0.1.6"
-      }
-    },
-    "videojs-contrib-media-sources": {
-      "version": "4.7.2",
-      "resolved": "https://registry.npm.taobao.org/videojs-contrib-media-sources/download/videojs-contrib-media-sources-4.7.2.tgz",
-      "integrity": "sha1-Ct+SkQfVt0zyyKuygkyCF35DhY4=",
-      "requires": {
-        "global": "^4.3.0",
-        "mux.js": "4.3.2",
-        "video.js": "^5.17.0 || ^6.2.0",
-        "webwackify": "0.1.6"
-      }
-    },
-    "videojs-flash": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npm.taobao.org/videojs-flash/download/videojs-flash-2.2.1.tgz",
-      "integrity": "sha1-GiJduxztIArpu/FeAf5KYQhtkPE=",
-      "requires": {
-        "global": "^4.4.0",
-        "video.js": "^6 || ^7",
-        "videojs-swf": "5.4.2"
-      },
-      "dependencies": {
-        "global": {
-          "version": "4.4.0",
-          "resolved": "https://registry.npm.taobao.org/global/download/global-4.4.0.tgz",
-          "integrity": "sha1-PnsQUXkAajI+1xqvyj6cV6XMZAY=",
-          "requires": {
-            "min-document": "^2.19.0",
-            "process": "^0.11.10"
-          }
-        },
-        "process": {
-          "version": "0.11.10",
-          "resolved": "https://registry.npm.taobao.org/process/download/process-0.11.10.tgz",
-          "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
-        }
-      }
-    },
-    "videojs-font": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npm.taobao.org/videojs-font/download/videojs-font-2.1.0.tgz",
-      "integrity": "sha1-olkwpn9snPvyu4jay4xrRR8JM3k="
-    },
-    "videojs-hotkeys": {
-      "version": "0.2.27",
-      "resolved": "https://registry.npm.taobao.org/videojs-hotkeys/download/videojs-hotkeys-0.2.27.tgz?cache=0&sync_timestamp=1596424673437&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvideojs-hotkeys%2Fdownload%2Fvideojs-hotkeys-0.2.27.tgz",
-      "integrity": "sha1-Dfl5Urnf8ObMHPikOf7X6snHPwE="
-    },
-    "videojs-ie8": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npm.taobao.org/videojs-ie8/download/videojs-ie8-1.1.2.tgz",
-      "integrity": "sha1-oj09hgitcZK2nGB3/E64SJmNNdk=",
-      "requires": {
-        "es5-shim": "^4.5.1"
-      }
-    },
-    "videojs-swf": {
-      "version": "5.4.2",
-      "resolved": "https://registry.npm.taobao.org/videojs-swf/download/videojs-swf-5.4.2.tgz",
-      "integrity": "sha1-aWSpv/kDtzLz5GUxSuR4oCoX6Ks="
-    },
-    "videojs-vtt.js": {
-      "version": "0.12.6",
-      "resolved": "https://registry.npm.taobao.org/videojs-vtt.js/download/videojs-vtt.js-0.12.6.tgz",
-      "integrity": "sha1-4HhgC9qJnqpvnDMHE0zQyBGUe44=",
-      "requires": {
-        "global": "^4.3.1"
-      }
-    },
-    "vue-aliplayer": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/vue-aliplayer/-/vue-aliplayer-1.0.0.tgz",
-      "integrity": "sha512-z29s38hlNJDckGSPtuTsYwMdjj70SsvJ5VzbEoBoV2BTrg3ucvodM2CW7BWstrG9WaQqz4F8nVGLSON05RrmJw==",
-      "requires": {
-        "vue-github-badge": "^1.0.0"
-      }
-    },
-    "vue-github-badge": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/vue-github-badge/-/vue-github-badge-1.0.1.tgz",
-      "integrity": "sha1-3/fOBzIOZKIY7fEGsVpDF27AYQY="
-    },
-    "vue-video-player": {
-      "version": "5.0.2",
-      "resolved": "https://registry.npm.taobao.org/vue-video-player/download/vue-video-player-5.0.2.tgz",
-      "integrity": "sha1-NKQiOf8wTvx2mNogpBZQUddmweY=",
-      "requires": {
-        "object-assign": "^4.1.1",
-        "video.js": "^6.6.0",
-        "videojs-contrib-hls": "^5.12.2",
-        "videojs-flash": "^2.1.0",
-        "videojs-hotkeys": "^0.2.20"
-      }
-    },
-    "webwackify": {
-      "version": "0.1.6",
-      "resolved": "https://registry.npm.taobao.org/webwackify/download/webwackify-0.1.6.tgz",
-      "integrity": "sha1-HUKhKsYYI9fjRaveCE6qpipKles="
-    },
-    "which-module": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
-      "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
-    },
-    "wrap-ansi": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
-      "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
-      "requires": {
-        "ansi-styles": "^3.2.0",
-        "string-width": "^3.0.0",
-        "strip-ansi": "^5.0.0"
-      }
-    },
-    "xhr": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npm.taobao.org/xhr/download/xhr-2.4.0.tgz",
-      "integrity": "sha1-4W5mpF+GmGHu76tBbV7/ci3ECZM=",
-      "requires": {
-        "global": "~4.3.0",
-        "is-function": "^1.0.1",
-        "parse-headers": "^2.0.0",
-        "xtend": "^4.0.0"
-      }
-    },
-    "xtend": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npm.taobao.org/xtend/download/xtend-4.0.2.tgz",
-      "integrity": "sha1-u3J3n1+kZRhrH0OPZ0+jR/2121Q="
-    },
-    "y18n": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
-      "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
-    },
-    "yargs": {
-      "version": "13.3.2",
-      "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
-      "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
-      "requires": {
-        "cliui": "^5.0.0",
-        "find-up": "^3.0.0",
-        "get-caller-file": "^2.0.1",
-        "require-directory": "^2.1.1",
-        "require-main-filename": "^2.0.0",
-        "set-blocking": "^2.0.0",
-        "string-width": "^3.0.0",
-        "which-module": "^2.0.0",
-        "y18n": "^4.0.0",
-        "yargs-parser": "^13.1.2"
-      }
-    },
-    "yargs-parser": {
-      "version": "13.1.2",
-      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
-      "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
-      "requires": {
-        "camelcase": "^5.0.0",
-        "decamelize": "^1.2.0"
-      }
-    }
-  }
+  "lockfileVersion": 1
 }

+ 62 - 37
pages/homework/setHomework.vue

@@ -1,26 +1,34 @@
 <template>
 	<view class="center">
-		<view class="add_picture" v-if="showVideo" >
+		<view>上传视频</view>
+		<view class="add_picture" v-if="src" >
 			<video :src="src" ></video>
 			<view @click="DelImg" class="x">x</view>
 		</view>
 		<view v-else class="add_picture">
 			<image src="https://zhibo.liuniu946.com/img/phone.png"  @click="test"></image>
 		</view>
+		<view>上传图片</view>
+		<view class="add_picture">
+			<image :src="cardimg" mode="" class="upload-img" @click.stop="imgsub" v-if="cardimg"></image>
+			<image src="https://zhibo.liuniu946.com/img/phone.png" class="upload-img" mode="" v-if="!cardimg" @click.stop="imgsub"></image>
+		</view>
 		<view class="buttom" @click="submit()">上传作业</view>
 	</view>
 </template>
 
 <script>
-import { sethomework } from '@/api/homework.js'
+import { sethomework } from '@/api/homework.js';
+import {
+		uploads, upvideo
+	} from '@/api/apply.js';
 export default {
 	data(){
 		return{
 			id: '',
-			showVideo: false,
-			addVideo: false,
 			src:'',
 			key:'',
+			cardimg:'',
 			token:''//七牛云的token
 		}
 	},
@@ -28,6 +36,16 @@ export default {
 		this.id = option.id
 	},
 	methods:{
+		// 上传图片
+		imgsub() {
+			console.log('上传图片')
+			uploads({
+				filename: ''
+			}).then(data => {
+				console.log("data",data);
+				this.cardimg = data[0].url;
+			})
+		},
 		submit(){
 			sethomework({
 				timetable_id: this.id,
@@ -38,40 +56,47 @@ export default {
 				console.log(err)
 			})
 		},
-		test: function () {
-					    var self = this;
-					    uni.chooseVideo({
-					        count: 1,
-					        sourceType: ['camera', 'album'],
-					        success: function (res) {
-								console.log("选择视频成功",res)
-								self.showVideo=true
-								self.addVideo=false
-					            self.src = res.tempFilePath;
-								uni.uploadFile({
-									url: 'https://up-z2.qiniup.com', //仅为示例,非真实的接口地址
-									filePath: res.tempFilePath,
-									name: 'files',
-									formData: {
-									    'token':self.token,
-										'key':self.key
-									},
-									header: {
-										Authorization: 'Bearer ' + uni.getStorageSync('access')
-									},
-									 success: (uploadFileRes) => {
-										console.log('1张', uploadFileRes);
-										let bold = JSON.parse(uploadFileRes.data)
-										console.log(bold)
-										console.log(bold.result[0].filePath)
-										self.src = bold.result[0].filePath + bold.result[0].fileName
-										console.log("this.returnImage", self.returnImage)
-									},
+		test(){
+			console.log('上传视频')
+			upvideo({
+				filename: ''
+			}).then(data => {
+				console.log("data",data);
+				// this.src = data[0].url;
+			})
+		},
+	// 	test: function () {
+	// 				    var self = this;
+	// 				    uni.chooseVideo({
+	// 						count: 1,
+	// 						sourceType: ['camera', 'album'],
+	// 				        success: function (res) {
+	// 							console.log("选择视频成功",res)
+	// 				            self.src = res.tempFilePath;
+	// 							uni.uploadFile({
+	// 								url: 'https://up-z2.qiniup.com', //仅为示例,非真实的接口地址
+	// 								filePath: res.tempFilePath,
+	// 								name: 'files',
+	// 								formData: {
+	// 								    'token':self.token,
+	// 									'key':self.key
+	// 								},
+	// 								header: {
+	// 									Authorization: 'Bearer ' + uni.getStorageSync('access')
+	// 								},
+	// 								 success: (uploadFileRes) => {
+	// 									console.log('1张', uploadFileRes);
+	// 									let bold = JSON.parse(uploadFileRes.data)
+	// 									console.log(bold)
+	// 									console.log(bold.result[0].filePath)
+	// 									self.src = bold.result[0].filePath + bold.result[0].fileName
+	// 									console.log("this.returnImage", self.returnImage)
+	// 								},
 								
-								});
-					        }
-					    });
-					},
+	// 							});
+	// 				        }
+	// 				    });
+	// 				},
 		//右上角删除视频
 			DelImg(){
 						this.src='',

+ 22 - 2
pages/index/index.vue

@@ -2,7 +2,11 @@
 	<view class="center">
 		<!-- <view class="cl"></view> -->
 		<view class="carousel-section">
-			<image src="../../static/img/index.jpg"></image>
+			<swiper class="carousel" autoplay="true" interval="5000" duration="400" @change="swiperChange">
+				<swiper-item v-for="(item, index) in carouselList" :key="index" class="carousel-item" @click="bannerNavToUrl(item)"> 
+					<image :src="item.pic" />
+				</swiper-item>	
+			</swiper>
 		</view>
 		<view class="navBox">
 			<view class="navBox-item" @click="nav('/pages/course/course')">
@@ -44,9 +48,11 @@
 </template>
 
 <script>
+	import { banner } from '@/api/index.js'
 export default {
 	data(){
 		return{
+			swiperCurrent: 0,
 			carouselList: [],
 			dataList: [
 				{
@@ -72,12 +78,26 @@ export default {
 			]
 		}
 	},
+	onLoad() {
+		this.loadData();
+	},
 	methods: {
 		nav(url){
 			uni.navigateTo({
 				url: url,
 			})
-		}
+		},
+		async loadData(){
+			const obj = this;
+			banner({}).then(e => {
+				obj.carouselList = e.data.banner;
+			})
+		},
+		swiperChange(e) {
+			const index = e.detail.current;
+			this.swiperCurrent = index;
+			this.titleNViewBackground = this.carouselList[index].background;
+		},
 	}
 }
 </script>

+ 23 - 0
qiniu-js/.babelrc

@@ -0,0 +1,23 @@
+{
+  "presets": [
+    [
+      "@babel/preset-env",
+      {
+        "targets": {
+          "browsers": ["last 2 versions", "ie >= 8"]
+        },
+        "modules": "commonjs"
+      }
+    ]
+  ],
+  "plugins": [
+    [
+      "@babel/transform-runtime",
+      {
+        "corejs": 2
+      }
+    ],
+    "@babel/proposal-object-rest-spread"
+  ],
+  "ignore": ["**/*.d.ts", "**/*.js.map"]
+}

+ 5 - 0
qiniu-js/.eslintignore

@@ -0,0 +1,5 @@
+/src/__mock__
+/src/__tests__
+/test/*
+dist
+*.js

+ 13 - 0
qiniu-js/.eslintrc.js

@@ -0,0 +1,13 @@
+module.exports = {
+  extends: [
+    '@qiniu'
+  ],
+  settings: {
+    "import/resolver": {
+      node: {
+        extensions: ['.js', '.ts'],
+        moduleDirectory: ['node_modules', 'src/']
+      }
+    }
+  }
+}

+ 22 - 0
qiniu-js/.travis.yml

@@ -0,0 +1,22 @@
+language: node_js
+node_js:
+- '12'
+cache:
+  directories:
+  - node_modules
+install:
+- npm install
+script:
+- npm run build
+- jest --coverage
+- npx codecov
+before_deploy: npm run build
+deploy:
+  provider: npm
+  email: sdk@qiniu.com
+  skip_cleanup: true
+  api_key:
+    secure: L9V1k9Y6CFC5fIBRbGbpvhpb004kI9Z/Wvwp5xGnVuGO3n6OfEVoaX7gSLpOkyx4kZfzEEh+R9xriJAeXBfLjiigP1FrKm+EQ92VlkjGh+Dlc4NDwEO00RczfUxI4bQG/HVaVTsQs+EqCnkfsp403wECwuBS68zYtsJZldREmDo=
+  on:
+    tags: true
+    repo: qiniu/js-sdk

+ 507 - 0
qiniu-js/README.md

@@ -0,0 +1,507 @@
+# Qiniu-JavaScript-SDK
+
+[![Build Status](https://travis-ci.org/qiniu/x.svg?branch=master)](https://travis-ci.org/qiniu/js-sdk)
+[![GitHub release](https://img.shields.io/github/v/tag/qiniu/js-sdk.svg?label=release)](https://github.com/qiniu/js-sdk/releases)
+[![Coverage Status](https://codecov.io/gh/qiniu/js-sdk/branch/master/graph/badge.svg)](https://codecov.io/gh/qiniu/js-sdk)
+
+基于七牛 API 开发的前端 JavaScript SDK
+
+### 当前版本为 3.x,旧版本文档:[2.x](https://github.com/qiniu/js-sdk/tree/2.x)、[1.x](https://github.com/qiniu/js-sdk/tree/1.x)
+### 2.x 升级到 3.x 的注意事项请参考[文档](https://github.com/qiniu/js-sdk/wiki/2.x-%E5%8D%87%E7%BA%A7%E5%88%B0-3.x-%E6%96%87%E6%A1%A3%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9)
+
+## 快速导航
+
+* [功能简介](#summary)
+* [准备](#ready)
+* [引入](#install)
+* [使用](#usage)
+* [运行示例](#demo)
+* [说明](#note)
+* [常见问题](#faq)
+
+
+## 概述
+
+Qiniu-JavaScript-SDK (下文简称为 JS-SDK)适用于 :IE11、Edge、Chrome、Firefox、Safari 等浏览器,基于七牛云存储官方 API 构建,其中上传功能基于 H5 File API。开发者基于 JS-SDK 可以方便的从浏览器端上传文件至七牛云存储,并对上传成功后的图片进行丰富的数据处理操作。
+JS-SDK 兼容支持 H5 File API 的浏览器,在低版本浏览器下,需要额外的插件如 plupload,JS-SDK 提供了一些接口可以结合插件来进行上传工作,注意:(在低版本浏览器需要引入 [babel-polyfill](https://babeljs.cn/docs/usage/polyfill/) 来解决 sdk 里某些语法或者属性浏览器不能识别的问题)。
+
+Qiniu-JavaScript-SDK 为客户端 SDK,没有包含 `token` 生成实现,为了安全,`token` 建议通过网络从服务端获取,具体生成代码可以参考以下服务端 SDK 的文档。
+
+* [Android](https://developer.qiniu.com/kodo/sdk/android)
+* [Java](https://developer.qiniu.com/kodo/sdk/java)
+* [PHP](https://developer.qiniu.com/kodo/sdk/php)
+* [Python](https://developer.qiniu.com/kodo/sdk/python)
+* [Ruby](https://developer.qiniu.com/kodo/sdk/ruby)
+* [Go](https://developer.qiniu.com/kodo/sdk/go)
+* [Node.js](https://developer.qiniu.com/kodo/sdk/nodejs)
+* [C#](https://developer.qiniu.com/kodo/sdk/csharp)
+* [C/C++](https://developer.qiniu.com/kodo/sdk/cpp)
+* [Objective-C](https://developer.qiniu.com/kodo/sdk/objc)
+
+Qiniu-JavaScript-SDK 的示例 [Demo](http://jssdk-v2.demo.qiniu.io) 中的服务器端部分是基于[ Node.js 服务器端 SDK ](https://developer.qiniu.com/kodo/sdk/nodejs) 开发的。
+
+- [JavaScript SDK 在线示例](http://jssdk-v2.demo.qiniu.io/)
+<!--
+本 SDK 可使开发者忽略上传底层实现细节,而更多的关注 UI 层的展现。
+ -->
+<a id="summary"></a>
+
+## 功能简介
+
+* 上传
+  * 大于 4M 时可分块上传,小于 4M 时直传
+  * 分块上传时,支持断点续传
+* 数据处理(图片)
+  * imageView2(缩略图)
+  * imageMogr2(高级处理,包含缩放、裁剪、旋转等)
+  * imageInfo (获取基本信息)
+  * exif (获取图片 EXIF 信息)
+  * watermark (文字、图片水印)
+  * pipeline (管道,可对 imageView2、imageMogr2、watermark 进行链式处理)
+
+<a id="ready"></a>
+
+## 准备
+
+* 在使用 JS-SDK 之前,您必须先注册一个七牛帐号,并登录控制台获取一对有效的 `AccessKey` 和 `SecretKey`,您可以阅读[ 快速入门 ](https://developer.qiniu.com/kodo/manual/console-quickstart)和[ 安全机制 ](https://developer.qiniu.com/kodo/manual/security#security) 以进一步了解如何正确使用和管理密钥 。
+
+* JS-SDK 依赖服务端颁发 `token`,可以通过以下二种方式实现:
+
+  * 利用[七牛服务端 SDK ](https://developer.qiniu.com/sdk#sdk)构建后端服务
+  * 利用七牛底层 API 构建服务,详见七牛[上传策略](https://developer.qiniu.com/kodo/manual/put-policy)和[上传凭证](https://developer.qiniu.com/kodo/manual/upload-token)
+
+  前端通过接口请求以获得 `token` 信息
+
+<a id="install"></a>
+
+## 引入
+
+支持以下几种安装方式
+
+* 直接使用静态文件地址:
+
+  ```
+  https://cdnjs.cloudflare.com/ajax/libs/qiniu-js/<version>/qiniu.min.js
+  ```
+  通过sctipt标签引入该文件,会在全局生成名为 `qiniu` 的对象
+
+* 使用 NPM 安装
+
+  NPM 的全称是 Node Package Manager,是一个[ NodeJS ](https://nodejs.org)包管理和分发工具,已经成为了非官方的发布 Node 模块(包)的标准。如果需要更详细的关于 NPM 的使用说明,您可以访问[ NPM 官方网站](https://www.npmjs.com),或对应的[中文网站](http://www.npmjs.com.cn/)
+
+  ```
+  npm install qiniu-js
+  ```
+  ```Javascript
+  const qiniu = require('qiniu-js')
+  // or
+  import * as qiniu from 'qiniu-js'
+  ```
+
+* 通过源码编译
+
+`git clone git@github.com:qiniu/js-sdk.git`,进入项目根目录执行 `npm install` ,执行 `npm run build`,即可在dist 目录生成 `qiniu.min.js`。
+
+<a id="usage"></a>
+
+## 使用
+
+`qiniu.upload` 返回一个 `observable` 对象用来控制上传行为,`observable` 对像通过 `subscribe` 方法可以被 `observer` 所订阅,订阅同时会开始触发上传,同时返回一个 `subscription` 对象,该对象有一个 `unsubscribe` 方法取消订阅,同时终止上传行为。对于不支持 sdk 的浏览器可以参考 demo1 中用插件处理和 form 直传的方式; 一般 form 提交常常会导致网页跳转,demo1 中 form 直传通过加入 iframe,并结合后端 sdk 上传来解决网页跳转问题,实现 form 无刷新上传。分片上传时,JS-SDK支持断点续传功能,会把已上传片的后端返回值ctx信息存储到本地,有效期为一天,超过一天后,当继续上传该文件时会清除掉本地存储信息重新上传。
+
+### Example
+
+文件上传:
+
+```JavaScript
+
+const observable = qiniu.upload(file, key, token, putExtra, config)
+
+const subscription = observable.subscribe(observer) // 上传开始
+// or
+const subscription = observable.subscribe(next, error, complete) // 这样传参形式也可以
+
+subscription.unsubscribe() // 上传取消
+```
+图片上传前压缩:
+
+```JavaScript
+const options = {
+  quality: 0.92,
+  noCompressIfLarger: true
+  // maxWidth: 1000,
+  // maxHeight: 618
+}
+qiniu.compressImage(file, options).then(data => {
+  const observable = qiniu.upload(data.dist, key, token, putExtra, config)
+  const subscription = observable.subscribe(observer) // 上传开始
+})
+```
+## API Reference Interface
+
+### qiniu.upload(file: File, key: string, token: string, putExtra?: object, config?: object): observable
+
+  * **observable**: 为一个带有 subscribe 方法的类实例
+
+    * observable.subscribe(observer: object): subscription
+
+      * observer: `observer` 为一个 `object`,用来设置上传过程的监听函数,有三个属性 `next`、`error`、`complete`:
+
+        ```JavaScript
+        const observer = {
+          next(res){
+            // ...
+          },
+          error(err){
+            // ...
+          },
+          complete(res){
+            // ...
+          }
+        }
+        ```
+        * next: 接收上传进度信息的回调函数,回调函数参数值为 `object`,包含字段信息如下:
+          * uploadInfo: `object`,只有分片上传时才返回该字段
+            * uploadInfo.id: 上传任务的唯一标识。
+            * uploadInfo.url: 上传地址。
+          * total: 包含`loaded`、`total`、`percent`三个属性:
+            * total.loaded: `number`,已上传大小,单位为字节。
+            * total.total: `number`,本次上传的总量控制信息,单位为字节,注意这里的 total 跟文件大小并不一致。
+            * total.percent: `number`,当前上传进度,范围:0~100。
+
+        * error: 上传错误后触发;自动重试本身并不会触发该错误,而当重试次数到达上限后则可以触发。当不是 xhr 请求错误时,会把当前错误产生原因直接抛出,诸如 JSON 解析异常等;当产生 xhr 请求错误时,参数 err 为一个包含 `code`、`message`、`isRequestError` 三个属性的 `object`:
+          * err.isRequestError: 用于区分是否 xhr 请求错误;当 xhr 请求出现错误并且后端通过 HTTP 状态码返回了错误信息时,该参数为 `true`;否则为 `undefined` 。
+          * err.reqId: `string`,xhr请求错误的 `X-Reqid`。
+          * err.code: `number`,请求错误状态码,只有在 `err.isRequestError` 为 true 的时候才有效。可查阅码值对应[说明](https://developer.qiniu.com/kodo/api/3928/error-responses)。
+          * err.message: `string`,错误信息,包含错误码,当后端返回提示信息时也会有相应的错误信息。
+
+        * complete: 接收上传完成后的后端返回信息,具体返回结构取决于后端sdk的配置,可参考[上传策略](https://developer.qiniu.com/kodo/manual/1206/put-policy)。
+
+      * subscription: 为一个带有 `unsubscribe` 方法的类实例,通过调用 `subscription.unsubscribe()` 停止当前文件上传。
+
+  * **bucket**: 上传的目标空间
+  * **file**: `File` 对象,上传的文件
+  * **key**: 文件资源名,为空字符串时则文件资源名也为空,为 `null` 或者 `undefined` 时则自动使用文件的 `hash` 作为文件名
+  * **token**: 上传验证信息,前端通过接口请求后端获得
+  * **config**: `object`,其中的每一项都为可选
+
+    ```JavaScript
+    const config = {
+      useCdnDomain: true,
+      region: qiniu.region.z2
+    };
+    ```
+
+    * config.useCdnDomain: 表示是否使用 cdn 加速域名,为布尔值,`true` 表示使用,默认为 `false`。
+    * config.disableStatisticsReport: 是否禁用日志报告,为布尔值,默认为 `false`。
+    * config.uphost: 上传 `host`,类型为 `string`, 如果设定该参数则优先使用该参数作为上传地址,默认为 `null`。
+    * config.upprotocol: 自定义上传域名协议,值为 `https:` 或者 `http:`,默认为 `https:`。
+    * config.region: 选择上传域名区域;当为 `null` 或 `undefined` 时,自动分析上传域名区域。
+    * config.retryCount: 上传自动重试次数(整体重试次数,而不是某个分片的重试次数);默认 3 次(即上传失败后最多重试两次)。
+    * config.concurrentRequestLimit: 分片上传的并发请求量,`number`,默认为3;因为浏览器本身也会限制最大并发量,所以最大并发量与浏览器有关。
+    * config.checkByMD5: 是否开启 MD5 校验,为布尔值;在断点续传时,开启 MD5 校验会将已上传的分片与当前分片进行 MD5 值比对,若不一致,则重传该分片,避免使用错误的分片。读取分片内容并计算 MD5 需要花费一定的时间,因此会稍微增加断点续传时的耗时,默认为 false,不开启。
+    * config.forceDirect: 是否上传全部采用直传方式,为布尔值;为 `true` 时则上传方式全部为直传 form 方式,禁用断点续传,默认 `false`。
+    * config.chunkSize: `number`,分片上传时每片的大小,必须为正整数,单位为 `MB`,且最大不能超过 1024,默认值 4。因为 chunk 数最大 10000,所以如果文件以你所设的 `chunkSize` 进行分片并且 chunk 数超过 10000,我们会把你所设的 `chunkSize` 扩大二倍,如果仍不符合则继续扩大,直到符合条件。
+    * config.debugLogLevel: `INFO` | `WARN` | `ERROR` | `OFF`,允许程序在控制台输出日志,默认为 `OFF`,不输出任何日志,本功能仅仅用于本地调试,不建议在线上环境开启。
+
+  * **putExtra**: `object`,其中的每一项都为可选
+
+    ```JavaScript
+    const putExtra = {
+      fname: "qiniu.txt",
+      mimeType: "text/plain",
+      customVars: { 'x:test': 'qiniu', ... },
+      metadata: { 'x-qn-meta': 'qiniu', ... },
+    };
+    ```
+
+    * fname: `string`,文件原始文件名,若未指定,则魔法变量中无法使用 fname、ext、suffix
+    * customVars: `object`,用来放置自定义变量,变量名必须以 `x:` 开始,自定义变量格式及说明请参考[文档](https://developer.qiniu.com/kodo/manual/1235/vars)
+    * metadata: `object`,用来防止自定义 meta,变量名必须以 `x-qn-meta-`开始,自定义资源信息格式及说明请参考
+    [文档](https://developer.qiniu.com/kodo/api/1252/chgm)
+    * mimeType: `string`,指定所传的文件类型
+
+### qiniu.region: object
+
+  * **qiniu.region.z0**: 代表华东区域
+  * **qiniu.region.z1**: 代表华北区域
+  * **qiniu.region.z2**: 代表华南区域
+  * **qiniu.region.na0**: 代表北美区域
+  * **qiniu.region.as0**: 代表新加坡区域
+
+### qiniu.getUploadUrl(config: object, token: string): Promise
+
+  接收参数为 `config` 对象,返回根据 `config` 里所配置信息的上传域名
+
+  ```JavaScript
+  qiniu.getUploadUrl(config, token).then(res => {}) // res 即为上传的 url
+  ```
+
+### qiniu.getHeadersForChunkUpload(token: string): object
+
+  返回 `object`,包含用来获得分片上传设置的头信息,参数为 `token` 字符串;当分片上传时,请求需要带该函数返回的头信息
+  * **token**: 后端返回的上传验证信息
+
+  ```JavaScript
+  const headers = qiniu.getHeadersForChunkUpload(token)
+  ```
+
+### qiniu.deleteUploadedChunks(token: string, key: stting, uploadInfo: object): Promise<void>
+  删除指定上传任务中已上传完成的片,`key` 为目标文件名,`uploadInfo` 可通过 `next` 的返回获取,`token` 由服务端生成
+
+### qiniu.compressImage(file: File, options: object): Promise<CompressResult> (上传前图片压缩)
+
+  ```JavaScript
+  const imgLink = qiniu.compressImage(file, options).then(res => {
+    // res : {
+    //   dist: 压缩后输出的 Blob 对象或原始的 File 对象,具体看下面的 options 配置
+    //   width: 压缩后的图片宽度
+    //   height: 压缩后的图片高度
+    // }
+    }
+  })
+  ```
+  * file: 要压缩的源图片,为 `File` 对象,支持 `image/png`、`image/jpeg`、`image/bmp`、`image/webp` 这几种图片类型
+  * options: `object`
+    * options.quality: `number`,图片压缩质量,在图片格式为 `image/jpeg` 或 `image/webp` 的情况下生效,其他格式不会生效,可以从 0 到 1 的区间内选择图片的质量。默认值 0.92
+    * options.maxWidh: `number`,压缩图片的最大宽度值
+    * options.maxHeight: `number`,压缩图片的最大高度值
+    (注意:当 `maxWidth` 和 `maxHeight` 都不设置时,则采用原图尺寸大小)
+    * options.noCompressIfLarger: `boolean`,为 `true` 时如果发现压缩后图片大小比原来还大,则返回源图片(即输出的 dist 直接返回了输入的 file);默认 `false`,即保证图片尺寸符合要求,但不保证压缩后的图片体积一定变小
+  * CompressResult: `object`,包含如下字段:
+    * dist: 压缩后输出的 Blob 对象或原始的 File 对象
+    * width: 压缩后的图片宽度
+    * height: 压缩后的图片高度
+
+### qiniu.watermark(options: object, key?: string, domain?: string): string(水印)
+
+  返回添加水印后的图片地址
+  * **key** : 文件资源名
+  * **domain**: 为七牛空间(bucket)对应的域名,选择某个空间后,可通过"空间设置->基本设置->域名设置"查看获取,前端可以通过接口请求后端得到
+
+
+  ```JavaScript
+
+  const imgLink = qiniu.watermark({
+       mode: 1, // 图片水印
+       image: 'http://www.b1.qiniudn.com/images/logo-2.png', // 图片水印的Url,mode = 1 时 **必需**
+       dissolve: 50, // 透明度,取值范围1-100,非必需,下同
+       gravity: 'SouthWest', // 水印位置,为以下参数[NorthWest、North、NorthEast、West、Center、East、SouthWest、South、SouthEast]之一
+       dx: 100,  // 横轴边距,单位:像素(px)
+       dy: 100 // 纵轴边距,单位:像素(px)
+   }, key, domain)
+
+  // imgLink 可以赋值给 html 的 img 元素的 src 属性,下同
+
+  // 若未指定key,可以通过以下方式获得完整的 imgLink,下同
+  // imgLink  =  '<domain>/<key>?' +  imgLink
+  // <domain> 为七牛空间(bucket)对应的域名,选择某个空间后,可通过"空间设置->基本设置->域名设置"查看获取
+
+  // 或者
+
+  const imgLink = qiniu.watermark({
+       mode: 2,  // 文字水印
+       text: 'hello world !', // 水印文字,mode = 2 时 **必需**
+       dissolve: 50,          // 透明度,取值范围1-100,非必需,下同
+       gravity: 'SouthWest',  // 水印位置,同上
+       fontsize: 500,         // 字体大小,单位: 缇
+       font: '黑体',           // 水印文字字体
+       dx: 100,               // 横轴边距,单位:像素(px)
+       dy: 100,               // 纵轴边距,单位:像素(px)
+       fill: '#FFF000'        // 水印文字颜色,RGB格式,可以是颜色名称
+   }, key, domain)
+  ```
+
+  options包含的具体水印参数解释见[水印(watermark)](https://developer.qiniu.com/dora/api/image-watermarking-processing-watermark)
+
+### qiniu.imageView2(options: object, key?: string, domain?: string): string (缩略)
+
+  返回处理后的图片url
+
+  ```JavaScript
+  const imgLink = qiniu.imageView2({
+     mode: 3,       // 缩略模式,共6种[0-5]
+     w: 100,        // 具体含义由缩略模式决定
+     h: 100,        // 具体含义由缩略模式决定
+     q: 100,        // 新图的图像质量,取值范围:1-100
+     format: 'png'  // 新图的输出格式,取值范围:jpg,gif,png,webp等
+   }, key, domain)
+  ```
+
+  options包含的具体缩略参数解释见[图片基本处理(imageView2)](https://developer.qiniu.com/dora/api/basic-processing-images-imageview2)
+
+### qiniu.imageMogr2(options: object, key?: string, domain?: string): string (图像高级处理)
+
+  返回处理后的图片url
+
+  ```JavaScript
+  const imgLink = qiniu.imageMogr2({
+     "auto-orient": true,      // 布尔值,是否根据原图EXIF信息自动旋正,便于后续处理,建议放在首位。
+     strip: true,              // 布尔值,是否去除图片中的元信息
+     thumbnail: '1000x1000'    // 缩放操作参数
+     crop: '!300x400a10a10',   // 裁剪操作参数
+     gravity: 'NorthWest',     // 裁剪锚点参数
+     quality: 40,              // 图片质量,取值范围1-100
+     rotate: 20,               // 旋转角度,取值范围1-360,缺省为不旋转。
+     format: 'png',            // 新图的输出格式,取值范围:jpg,gif,png,webp等
+     blur: '3x5'               // 高斯模糊参数
+   }, key, domain)
+  ```
+
+  options包含的具体高级图像处理参数解释见[图像高级处理(imageMogr2)](https://developer.qiniu.com/dora/api/the-advanced-treatment-of-images-imagemogr2)
+
+### qiniu.imageInfo(key: string, domain: string): Promise
+
+  ```JavaScript
+  qiniu.imageInfo(key, domain).then(res => {})
+  ```
+
+  具体 imageInfo 解释见[图片基本信息(imageInfo)](https://developer.qiniu.com/dora/api/pictures-basic-information-imageinfo)
+
+### qiniu.exif(key: string, domain: string): Promise
+
+  ```JavaScript
+  qiniu.exif(key, domain).then(res => {})
+  ```
+
+  具体 exif 解释见[图片 EXIF 信息(exif)](https://developer.qiniu.com/dora/api/photo-exif-information-exif)
+
+### qiniu.pipeline(fopArr: array, key?: string, domain?: string): string
+
+  ```JavaScript
+  const fopArr = [{
+      fop: 'watermark', // 指定watermark操作
+      mode: 2,          // 此参数同watermark函数的参数,下同。
+      text: 'hello world !',
+      dissolve: 50,
+      gravity: 'SouthWest',
+      fontsize: 500,
+      font : '黑体',
+      dx: 100,
+      dy: 100,
+      fill: '#FFF000'
+  },{
+      fop: 'imageView2', // 指定imageView2操作
+      mode: 3,           // 此参数同imageView2函数的参数,下同
+      w: 100,
+      h: 100,
+      q: 100,
+      format: 'png'
+  },{
+      fop: 'imageMogr2',  // 指定imageMogr2操作
+      "auto-orient": true,  // 此参数同imageMogr2函数的参数,下同。
+      strip: true,
+      thumbnail: '1000x1000'
+      crop: '!300x400a10a10',
+      gravity: 'NorthWest',
+      quality: 40,
+      rotate: 20,
+      format: 'png',
+      blur:'3x5'
+  }]
+
+  // fopArr 可以为三种类型'watermark'、'imageMogr2'、'imageView2'中的任意1-3个
+  // 例如只对'watermark'、'imageMogr2'进行管道操作,则如下即可
+  // const fopArr = [{
+  //    fop: 'watermark', // 指定watermark操作
+  //    mode: 2, // 此参数同watermark函数的参数,下同。
+  //    text: 'hello world !',
+  //    dissolve: 50,
+  //     gravity: 'SouthWest',
+  //     fontsize: 500,
+  //     font : '黑体',
+  //     dx: 100,
+  //     dy: 100,
+  //     fill: '#FFF000'
+  // },{
+  //    fop: 'imageMogr2',  // 指定imageMogr2操作
+  //    "auto-orient": true,  // 此参数同imageMogr2函数的参数,下同。
+  //    strip: true,
+  //    thumbnail: '1000x1000'
+  //    crop: '!300x400a10a10',
+  //    gravity: 'NorthWest',
+  //    quality: 40,
+  //    rotate: 20,
+  //    format: 'png',
+  //    blur:'3x5'
+  // }];
+
+  const imgLink = qiniu.pipeline(fopArr, key, domain))
+  ```
+
+  fopArr包含的具体管道操作解释见[管道操作](https://developer.qiniu.com/dora/manual/processing-mechanism)
+
+<a id="demo"></a>
+
+### 运行示例
+
+1. 进入 test 目录,按照目录下的 `config.json.example` 示例,创建 `config.json` 文件,其中,`Access Key` 和 `Secret Key` 按如下方式获取
+
+   * [开通七牛开发者帐号](https://portal.qiniu.com/signup)
+   * [登录七牛开发者自助平台,查看 AccessKey 和 SecretKey](https://portal.qiniu.com/user/key) 。
+
+   ```javascript
+   {
+     "AccessKey": "<Your Access Key>",
+     "SecretKey": "<Your Secret Key>",
+     "Bucket": "<Your Bucket Name>",
+     "Port": 9000,
+     "UptokenUrl": "<Your Uptoken_Url>", // demo 启动后会在本地 /uptoken 上提供获取 uptoken 的接口,所以这里可以填 'token'
+     "Domain": "<Your Bucket Domain>" // Bucket 的外链默认域名,在 Bucket 的内容管理里查看,如:'http://xxx.bkt.clouddn.com/'
+   }
+   ```
+2. 进入项目根目录,执行 `npm install` 安装依赖库,然后打开两个终端,一个执行 `npm run serve` 跑 server, 一个执行 `npm run dev` 运行服务;demo1:`http://0.0.0.0:8080/test/demo1`;demo3:`http://0.0.0.0:8080/test/demo3`;demo1为测试上传功能的示例,demo3为测试图片压缩功能的示例;demo2 为测试 es6 语法的示例,进入 demo2 目录,执行 `npm install`,然后 `npm start` 运行 demo2;demo1、demo2 和 demo3 都共用一个 server,请注意 server 文件里的 `region` 设置跟 `config` 里的` region` 设置要保持一致。
+
+
+<a id="note"></a>
+
+### 说明
+
+1. 如果您想了解更多七牛的上传策略,建议您仔细阅读 [七牛官方文档-上传](https://developer.qiniu.com/kodo/manual/upload-types)。另外,七牛的上传策略是在后端服务指定的.
+
+2. 如果您想了解更多七牛的图片处理,建议您仔细阅读 [七牛官方文档-图片处理](https://developer.qiniu.com/dora/api/image-processing-api)
+
+3. JS-SDK 示例生成 `token` 时,指定的 `Bucket Name` 为公开空间,所以可以公开访问上传成功后的资源。若您生成 `token` 时,指定的 `Bucket Name` 为私有空间,那您还需要在服务端进行额外的处理才能访问您上传的资源。具体参见[下载凭证](https://developer.qiniu.com/kodo/manual/download-token)。JS-SDK 数据处理部分功能不适用于私有空间。
+
+<a id="faq"></a>
+
+### 常见问题
+
+**1. 关于上传文件命名问题,可以参考:**
+
+1. 上传的 scope 为 `bucket` 的形式,上传后文件资源名以设置的 `key` 为主,如果 `key` 为 `null` 或者 `undefined`,则文件资源名会以 hash 值作为资源名。
+2. 上传的 scope 为 `bucket:key` 的形式,上传文件本地的名字需要和 scope 中的 `key` 是一致的,不然会报错 key doesn‘t match with scope。
+3. 上传的 scope 为 `bucket`,但是 `token` 中有设定 `saveKey`,这种形式下客户端的 `key` 如果设定为 `null` 或者 `undefined`,则会以 `saveKey` 作为文件资源名,否则仍然是以 `key` 值作为资源名,并且上传的本地文件名也是需要和这个 `savekey` 文件名一致的。
+
+**2. 限制上传文件的类型:**
+
+这里又分为两种方法:
+
+1. 通过在 `token` 中设定 `mimeLimit` 字段限定上传文件的类型,该设定是在后端 sdk 设置,请查看相应的 sdk 文档,示例
+ ```JavaScript
+"image/\*": 表示只允许上传图片类型;
+"image/jpeg;image/png": 表示只允许上传 jpg 和 png 类型的图片;
+"!application/json;text/plain": 表示禁止上传 json 文本和纯文本。(注意最前面的感叹号)
+```
+2. 通过 `putExtra` 的 `mimeType` 参数直接在前端限定
+
+### 贡献代码
+
+1. 登录 https://github.com
+
+2. Fork git@github.com:qiniu/js-sdk.git
+
+3. 创建您的特性分支 (git checkout -b new-feature)
+
+4. 提交您的改动 (git commit -am 'Added some features or fixed a bug')
+
+5. 将您的改动记录提交到远程 git 仓库 (git push origin new-feature)
+
+6. 然后到 github 网站的该 git 远程仓库的 new-feature 分支下发起 Pull Request
+
+<a id="license"></a>
+
+### 许可证
+
+> Copyright (c) 2018 qiniu.com
+
+### 基于 MIT 协议发布

+ 541 - 0
qiniu-js/coverage/clover.xml

@@ -0,0 +1,541 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<coverage generated="1619055534112" clover="3.2.0">
+  <project timestamp="1619055534112" name="All files">
+    <metrics statements="499" coveredstatements="217" conditionals="264" coveredconditionals="83" methods="78" coveredmethods="29" elements="841" coveredelements="329" complexity="0" loc="499" ncloc="499" packages="3" files="9" classes="9"/>
+    <package name="src">
+      <metrics statements="383" coveredstatements="170" conditionals="197" coveredconditionals="56" methods="60" coveredmethods="20"/>
+      <file name="api.ts" path="/home/travis/build/qiniu/js-sdk/src/api.ts">
+        <metrics statements="35" coveredstatements="22" conditionals="12" coveredconditionals="6" methods="7" coveredmethods="2"/>
+        <line num="1" count="1" type="stmt"/>
+        <line num="2" count="1" type="stmt"/>
+        <line num="3" count="1" type="stmt"/>
+        <line num="16" count="1" type="stmt"/>
+        <line num="17" count="1" type="stmt"/>
+        <line num="18" count="1" type="stmt"/>
+        <line num="19" count="1" type="stmt"/>
+        <line num="25" count="1" type="stmt"/>
+        <line num="26" count="5" type="cond" truecount="1" falsecount="1"/>
+        <line num="28" count="5" type="cond" truecount="2" falsecount="0"/>
+        <line num="29" count="1" type="stmt"/>
+        <line num="32" count="4" type="cond" truecount="2" falsecount="0"/>
+        <line num="33" count="3" type="stmt"/>
+        <line num="34" count="3" type="cond" truecount="1" falsecount="1"/>
+        <line num="35" count="3" type="stmt"/>
+        <line num="38" count="1" type="stmt"/>
+        <line num="39" count="1" type="stmt"/>
+        <line num="40" count="1" type="stmt"/>
+        <line num="49" count="0" type="stmt"/>
+        <line num="50" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="66" count="1" type="stmt"/>
+        <line num="72" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="73" count="0" type="stmt"/>
+        <line num="93" count="1" type="stmt"/>
+        <line num="100" count="0" type="stmt"/>
+        <line num="101" count="0" type="stmt"/>
+        <line num="102" count="0" type="stmt"/>
+        <line num="117" count="1" type="stmt"/>
+        <line num="123" count="0" type="stmt"/>
+        <line num="124" count="0" type="stmt"/>
+        <line num="125" count="0" type="stmt"/>
+        <line num="137" count="1" type="stmt"/>
+        <line num="142" count="0" type="stmt"/>
+        <line num="143" count="0" type="stmt"/>
+        <line num="144" count="0" type="stmt"/>
+      </file>
+      <file name="base64.ts" path="/home/travis/build/qiniu/js-sdk/src/base64.ts">
+        <metrics statements="89" coveredstatements="44" conditionals="32" coveredconditionals="8" methods="5" coveredmethods="3"/>
+        <line num="3" count="3" type="stmt"/>
+        <line num="19" count="2" type="cond" truecount="3" falsecount="1"/>
+        <line num="20" count="0" type="stmt"/>
+        <line num="23" count="2" type="stmt"/>
+        <line num="24" count="2" type="stmt"/>
+        <line num="27" count="2" type="stmt"/>
+        <line num="29" count="2" type="stmt"/>
+        <line num="30" count="2" type="stmt"/>
+        <line num="31" count="2" type="stmt"/>
+        <line num="32" count="86" type="stmt"/>
+        <line num="33" count="86" type="stmt"/>
+        <line num="35" count="86" type="cond" truecount="1" falsecount="1"/>
+        <line num="36" count="86" type="stmt"/>
+        <line num="37" count="0" type="cond" truecount="0" falsecount="4"/>
+        <line num="38" count="0" type="stmt"/>
+        <line num="39" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="40" count="0" type="stmt"/>
+        <line num="47" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="48" count="0" type="stmt"/>
+        <line num="50" count="0" type="stmt"/>
+        <line num="51" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="52" count="0" type="stmt"/>
+        <line num="54" count="0" type="stmt"/>
+        <line num="55" count="0" type="stmt"/>
+        <line num="62" count="86" type="cond" truecount="1" falsecount="1"/>
+        <line num="63" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="64" count="0" type="stmt"/>
+        <line num="66" count="0" type="stmt"/>
+        <line num="67" count="0" type="stmt"/>
+        <line num="71" count="2" type="cond" truecount="1" falsecount="1"/>
+        <line num="72" count="2" type="stmt"/>
+        <line num="75" count="2" type="stmt"/>
+        <line num="78" count="3" type="stmt"/>
+        <line num="94" count="2" type="stmt"/>
+        <line num="103" count="2" type="stmt"/>
+        <line num="104" count="2" type="stmt"/>
+        <line num="105" count="2" type="stmt"/>
+        <line num="106" count="2" type="stmt"/>
+        <line num="108" count="2" type="cond" truecount="1" falsecount="1"/>
+        <line num="109" count="0" type="stmt"/>
+        <line num="112" count="2" type="stmt"/>
+        <line num="114" count="2" type="stmt"/>
+        <line num="116" count="30" type="stmt"/>
+        <line num="117" count="30" type="stmt"/>
+        <line num="118" count="30" type="stmt"/>
+        <line num="120" count="30" type="stmt"/>
+        <line num="122" count="30" type="stmt"/>
+        <line num="123" count="30" type="stmt"/>
+        <line num="124" count="30" type="stmt"/>
+        <line num="125" count="30" type="stmt"/>
+        <line num="128" count="30" type="stmt"/>
+        <line num="132" count="2" type="stmt"/>
+        <line num="134" count="2" type="stmt"/>
+        <line num="136" count="2" type="stmt"/>
+        <line num="137" count="2" type="stmt"/>
+        <line num="139" count="0" type="stmt"/>
+        <line num="140" count="0" type="stmt"/>
+        <line num="143" count="2" type="stmt"/>
+        <line num="146" count="3" type="stmt"/>
+        <line num="164" count="0" type="stmt"/>
+        <line num="165" count="0" type="stmt"/>
+        <line num="166" count="0" type="stmt"/>
+        <line num="167" count="0" type="stmt"/>
+        <line num="168" count="0" type="stmt"/>
+        <line num="170" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="171" count="0" type="stmt"/>
+        <line num="174" count="0" type="stmt"/>
+        <line num="176" count="0" type="stmt"/>
+        <line num="177" count="0" type="stmt"/>
+        <line num="178" count="0" type="stmt"/>
+        <line num="179" count="0" type="stmt"/>
+        <line num="180" count="0" type="stmt"/>
+        <line num="182" count="0" type="stmt"/>
+        <line num="184" count="0" type="stmt"/>
+        <line num="185" count="0" type="stmt"/>
+        <line num="186" count="0" type="stmt"/>
+        <line num="188" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="189" count="0" type="stmt"/>
+        <line num="190" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="191" count="0" type="stmt"/>
+        <line num="193" count="0" type="stmt"/>
+        <line num="197" count="0" type="stmt"/>
+        <line num="199" count="0" type="stmt"/>
+        <line num="202" count="3" type="stmt"/>
+        <line num="203" count="2" type="stmt"/>
+        <line num="204" count="2" type="stmt"/>
+        <line num="207" count="3" type="stmt"/>
+        <line num="208" count="0" type="stmt"/>
+        <line num="209" count="0" type="stmt"/>
+      </file>
+      <file name="config.ts" path="/home/travis/build/qiniu/js-sdk/src/config.ts">
+        <metrics statements="2" coveredstatements="2" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0"/>
+        <line num="2" count="1" type="stmt"/>
+        <line num="11" count="1" type="stmt"/>
+      </file>
+      <file name="image.ts" path="/home/travis/build/qiniu/js-sdk/src/image.ts">
+        <metrics statements="97" coveredstatements="56" conditionals="94" coveredconditionals="40" methods="7" coveredmethods="4"/>
+        <line num="1" count="1" type="stmt"/>
+        <line num="2" count="1" type="stmt"/>
+        <line num="48" count="3" type="stmt"/>
+        <line num="49" count="3" type="cond" truecount="1" falsecount="1"/>
+        <line num="50" count="3" type="stmt"/>
+        <line num="53" count="3" type="stmt"/>
+        <line num="56" count="1" type="stmt"/>
+        <line num="57" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="58" count="0" type="stmt"/>
+        <line num="61" count="5" type="stmt"/>
+        <line num="63" count="1" type="cond" truecount="3" falsecount="1"/>
+        <line num="64" count="0" type="stmt"/>
+        <line num="67" count="1" type="stmt"/>
+        <line num="68" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="69" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="70" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="71" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="72" count="1" type="cond" truecount="3" falsecount="1"/>
+        <line num="73" count="1" type="stmt"/>
+        <line num="75" count="1" type="stmt"/>
+        <line num="79" count="1" type="stmt"/>
+        <line num="80" count="1" type="stmt"/>
+        <line num="81" count="8" type="stmt"/>
+        <line num="83" count="1" type="stmt"/>
+        <line num="85" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="86" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="87" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="88" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="89" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="90" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="91" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="92" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="93" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="94" count="1" type="cond" truecount="3" falsecount="1"/>
+        <line num="95" count="1" type="stmt"/>
+        <line num="97" count="1" type="stmt"/>
+        <line num="101" count="1" type="stmt"/>
+        <line num="102" count="2" type="stmt"/>
+        <line num="103" count="2" type="cond" truecount="1" falsecount="1"/>
+        <line num="104" count="0" type="stmt"/>
+        <line num="107" count="2" type="stmt"/>
+        <line num="108" count="2" type="cond" truecount="4" falsecount="0"/>
+        <line num="109" count="1" type="stmt"/>
+        <line num="112" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="113" count="1" type="stmt"/>
+        <line num="114" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="115" count="0" type="stmt"/>
+        <line num="117" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="120" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="121" count="0" type="stmt"/>
+        <line num="122" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="123" count="0" type="stmt"/>
+        <line num="125" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="126" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="127" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="128" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="131" count="4" type="stmt"/>
+        <line num="133" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="134" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="135" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="136" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="137" count="1" type="cond" truecount="3" falsecount="1"/>
+        <line num="138" count="1" type="stmt"/>
+        <line num="140" count="1" type="stmt"/>
+        <line num="144" count="1" type="stmt"/>
+        <line num="145" count="0" type="stmt"/>
+        <line num="146" count="0" type="stmt"/>
+        <line num="150" count="1" type="stmt"/>
+        <line num="151" count="0" type="stmt"/>
+        <line num="152" count="0" type="stmt"/>
+        <line num="155" count="1" type="stmt"/>
+        <line num="156" count="0" type="stmt"/>
+        <line num="158" count="0" type="stmt"/>
+        <line num="159" count="0" type="stmt"/>
+        <line num="160" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="161" count="0" type="stmt"/>
+        <line num="162" count="0" type="stmt"/>
+        <line num="163" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="164" count="0" type="stmt"/>
+        <line num="166" count="0" type="stmt"/>
+        <line num="168" count="0" type="stmt"/>
+        <line num="169" count="0" type="stmt"/>
+        <line num="171" count="0" type="stmt"/>
+        <line num="172" count="0" type="stmt"/>
+        <line num="174" count="0" type="stmt"/>
+        <line num="175" count="0" type="stmt"/>
+        <line num="177" count="0" type="stmt"/>
+        <line num="178" count="0" type="stmt"/>
+        <line num="180" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="181" count="0" type="stmt"/>
+        <line num="185" count="0" type="cond" truecount="0" falsecount="4"/>
+        <line num="186" count="0" type="stmt"/>
+        <line num="187" count="0" type="stmt"/>
+        <line num="188" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="189" count="0" type="stmt"/>
+        <line num="192" count="0" type="stmt"/>
+        <line num="195" count="0" type="stmt"/>
+      </file>
+      <file name="pool.ts" path="/home/travis/build/qiniu/js-sdk/src/pool.ts">
+        <metrics statements="22" coveredstatements="21" conditionals="0" coveredconditionals="0" methods="11" coveredmethods="10"/>
+        <line num="9" count="1" type="stmt"/>
+        <line num="10" count="1" type="stmt"/>
+        <line num="11" count="1" type="stmt"/>
+        <line num="13" count="1" type="stmt"/>
+        <line num="15" count="6" type="stmt"/>
+        <line num="16" count="6" type="stmt"/>
+        <line num="17" count="6" type="stmt"/>
+        <line num="22" count="6" type="stmt"/>
+        <line num="26" count="6" type="stmt"/>
+        <line num="27" count="12" type="stmt"/>
+        <line num="28" count="6" type="stmt"/>
+        <line num="29" count="6" type="stmt"/>
+        <line num="31" count="11" type="stmt"/>
+        <line num="32" count="6" type="stmt"/>
+        <line num="33" count="6" type="stmt"/>
+        <line num="35" count="0" type="stmt"/>
+        <line num="39" count="12" type="stmt"/>
+        <line num="40" count="12" type="stmt"/>
+        <line num="41" count="12" type="stmt"/>
+        <line num="42" count="12" type="stmt"/>
+        <line num="43" count="6" type="stmt"/>
+        <line num="46" count="1" type="stmt"/>
+      </file>
+      <file name="utils.ts" path="/home/travis/build/qiniu/js-sdk/src/utils.ts">
+        <metrics statements="138" coveredstatements="25" conditionals="59" coveredconditionals="2" methods="30" coveredmethods="1"/>
+        <line num="1" count="2" type="stmt"/>
+        <line num="3" count="2" type="stmt"/>
+        <line num="5" count="2" type="stmt"/>
+        <line num="8" count="2" type="stmt"/>
+        <line num="10" count="0" type="stmt"/>
+        <line num="12" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="13" count="0" type="stmt"/>
+        <line num="16" count="0" type="stmt"/>
+        <line num="17" count="0" type="stmt"/>
+        <line num="21" count="0" type="stmt"/>
+        <line num="22" count="0" type="stmt"/>
+        <line num="23" count="0" type="stmt"/>
+        <line num="24" count="0" type="stmt"/>
+        <line num="28" count="0" type="stmt"/>
+        <line num="30" count="0" type="stmt"/>
+        <line num="33" count="2" type="stmt"/>
+        <line num="34" count="0" type="stmt"/>
+        <line num="37" count="2" type="stmt"/>
+        <line num="38" count="0" type="stmt"/>
+        <line num="41" count="2" type="stmt"/>
+        <line num="42" count="0" type="stmt"/>
+        <line num="45" count="2" type="stmt"/>
+        <line num="46" count="0" type="stmt"/>
+        <line num="47" count="0" type="stmt"/>
+        <line num="49" count="0" type="stmt"/>
+        <line num="53" count="2" type="stmt"/>
+        <line num="54" count="2" type="cond" truecount="2" falsecount="0"/>
+        <line num="55" count="2" type="stmt"/>
+        <line num="58" count="2" type="stmt"/>
+        <line num="59" count="0" type="stmt"/>
+        <line num="60" count="0" type="stmt"/>
+        <line num="62" count="0" type="stmt"/>
+        <line num="66" count="2" type="stmt"/>
+        <line num="67" count="0" type="stmt"/>
+        <line num="68" count="0" type="stmt"/>
+        <line num="69" count="0" type="stmt"/>
+        <line num="71" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="72" count="0" type="stmt"/>
+        <line num="75" count="0" type="stmt"/>
+        <line num="76" count="0" type="stmt"/>
+        <line num="79" count="0" type="stmt"/>
+        <line num="80" count="0" type="stmt"/>
+        <line num="83" count="0" type="stmt"/>
+        <line num="86" count="2" type="stmt"/>
+        <line num="87" count="0" type="stmt"/>
+        <line num="88" count="0" type="stmt"/>
+        <line num="91" count="2" type="stmt"/>
+        <line num="92" count="0" type="stmt"/>
+        <line num="93" count="0" type="stmt"/>
+        <line num="99" count="2" type="stmt"/>
+        <line num="100" count="0" type="stmt"/>
+        <line num="101" count="0" type="stmt"/>
+        <line num="107" count="2" type="stmt"/>
+        <line num="108" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="109" count="0" type="stmt"/>
+        <line num="112" count="0" type="stmt"/>
+        <line num="115" count="2" type="stmt"/>
+        <line num="116" count="0" type="cond" truecount="0" falsecount="1"/>
+        <line num="117" count="0" type="stmt"/>
+        <line num="118" count="0" type="stmt"/>
+        <line num="119" count="0" type="stmt"/>
+        <line num="122" count="2" type="stmt"/>
+        <line num="123" count="0" type="stmt"/>
+        <line num="124" count="0" type="stmt"/>
+        <line num="126" count="0" type="stmt"/>
+        <line num="127" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="128" count="0" type="stmt"/>
+        <line num="129" count="0" type="stmt"/>
+        <line num="131" count="0" type="stmt"/>
+        <line num="135" count="0" type="stmt"/>
+        <line num="136" count="0" type="stmt"/>
+        <line num="139" count="0" type="stmt"/>
+        <line num="169" count="2" type="stmt"/>
+        <line num="170" count="0" type="stmt"/>
+        <line num="171" count="0" type="stmt"/>
+        <line num="172" count="0" type="stmt"/>
+        <line num="174" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="175" count="0" type="stmt"/>
+        <line num="178" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="179" count="0" type="stmt"/>
+        <line num="180" count="0" type="stmt"/>
+        <line num="181" count="0" type="stmt"/>
+        <line num="185" count="0" type="stmt"/>
+        <line num="186" count="0" type="cond" truecount="0" falsecount="4"/>
+        <line num="187" count="0" type="stmt"/>
+        <line num="194" count="0" type="stmt"/>
+        <line num="195" count="0" type="stmt"/>
+        <line num="196" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="197" count="0" type="stmt"/>
+        <line num="200" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="201" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="202" count="0" type="stmt"/>
+        <line num="203" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="204" count="0" type="stmt"/>
+        <line num="206" count="0" type="stmt"/>
+        <line num="212" count="0" type="stmt"/>
+        <line num="215" count="0" type="stmt"/>
+        <line num="216" count="0" type="stmt"/>
+        <line num="221" count="0" type="stmt"/>
+        <line num="225" count="0" type="stmt"/>
+        <line num="229" count="2" type="stmt"/>
+        <line num="230" count="0" type="cond" truecount="0" falsecount="4"/>
+        <line num="231" count="0" type="stmt"/>
+        <line num="233" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="234" count="0" type="stmt"/>
+        <line num="237" count="0" type="stmt"/>
+        <line num="238" count="0" type="stmt"/>
+        <line num="240" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="241" count="0" type="stmt"/>
+        <line num="244" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="245" count="0" type="stmt"/>
+        <line num="248" count="0" type="stmt"/>
+        <line num="251" count="0" type="stmt"/>
+        <line num="254" count="2" type="stmt"/>
+        <line num="255" count="0" type="cond" truecount="0" falsecount="4"/>
+        <line num="256" count="0" type="stmt"/>
+        <line num="257" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="260" count="0" type="stmt"/>
+        <line num="268" count="2" type="stmt"/>
+        <line num="269" count="0" type="stmt"/>
+        <line num="271" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="272" count="0" type="stmt"/>
+        <line num="274" count="0" type="stmt"/>
+        <line num="280" count="2" type="stmt"/>
+        <line num="281" count="0" type="cond" truecount="0" falsecount="3"/>
+        <line num="282" count="0" type="stmt"/>
+        <line num="291" count="2" type="stmt"/>
+        <line num="292" count="0" type="stmt"/>
+        <line num="294" count="0" type="stmt"/>
+        <line num="297" count="0" type="stmt"/>
+        <line num="304" count="0" type="stmt"/>
+        <line num="311" count="0" type="stmt"/>
+        <line num="318" count="0" type="stmt"/>
+        <line num="325" count="0" type="stmt"/>
+        <line num="332" count="0" type="stmt"/>
+        <line num="339" count="0" type="stmt"/>
+        <line num="346" count="0" type="stmt"/>
+        <line num="352" count="0" type="stmt"/>
+      </file>
+    </package>
+    <package name="src.logger">
+      <metrics statements="35" coveredstatements="34" conditionals="39" coveredconditionals="27" methods="8" coveredmethods="8"/>
+      <file name="index.ts" path="/home/travis/build/qiniu/js-sdk/src/logger/index.ts">
+        <metrics statements="24" coveredstatements="23" conditionals="12" coveredconditionals="10" methods="6" coveredmethods="6"/>
+        <line num="1" count="1" type="stmt"/>
+        <line num="5" count="1" type="stmt"/>
+        <line num="6" count="1" type="stmt"/>
+        <line num="10" count="9" type="stmt"/>
+        <line num="13" count="9" type="stmt"/>
+        <line num="14" count="9" type="cond" truecount="1" falsecount="1"/>
+        <line num="15" count="9" type="cond" truecount="1" falsecount="1"/>
+        <line num="23" count="1" type="stmt"/>
+        <line num="24" count="2" type="cond" truecount="2" falsecount="0"/>
+        <line num="25" count="1" type="stmt"/>
+        <line num="26" count="0" type="stmt"/>
+        <line num="33" count="12" type="stmt"/>
+        <line num="34" count="4" type="stmt"/>
+        <line num="35" count="4" type="cond" truecount="2" falsecount="0"/>
+        <line num="36" count="1" type="stmt"/>
+        <line num="44" count="12" type="stmt"/>
+        <line num="45" count="4" type="stmt"/>
+        <line num="46" count="4" type="cond" truecount="2" falsecount="0"/>
+        <line num="47" count="2" type="stmt"/>
+        <line num="55" count="12" type="stmt"/>
+        <line num="56" count="4" type="stmt"/>
+        <line num="57" count="4" type="cond" truecount="2" falsecount="0"/>
+        <line num="58" count="3" type="stmt"/>
+        <line num="61" count="1" type="stmt"/>
+      </file>
+      <file name="report-v3.ts" path="/home/travis/build/qiniu/js-sdk/src/logger/report-v3.ts">
+        <metrics statements="11" coveredstatements="11" conditionals="27" coveredconditionals="17" methods="2" coveredmethods="2"/>
+        <line num="1" count="1" type="stmt"/>
+        <line num="22" count="12" type="cond" truecount="2" falsecount="0"/>
+        <line num="23" count="11" type="stmt"/>
+        <line num="24" count="11" type="stmt"/>
+        <line num="25" count="11" type="stmt"/>
+        <line num="26" count="11" type="stmt"/>
+        <line num="27" count="11" type="stmt"/>
+        <line num="28" count="29" type="cond" truecount="5" falsecount="0"/>
+        <line num="29" count="7" type="stmt"/>
+        <line num="34" count="11" type="stmt"/>
+        <line num="47" count="11" type="stmt"/>
+      </file>
+    </package>
+    <package name="src.upload">
+      <metrics statements="81" coveredstatements="13" conditionals="28" coveredconditionals="0" methods="10" coveredmethods="1"/>
+      <file name="base.ts" path="/home/travis/build/qiniu/js-sdk/src/upload/base.ts">
+        <metrics statements="81" coveredstatements="13" conditionals="28" coveredconditionals="0" methods="10" coveredmethods="1"/>
+        <line num="2" count="1" type="stmt"/>
+        <line num="4" count="1" type="stmt"/>
+        <line num="6" count="1" type="stmt"/>
+        <line num="85" count="1" type="stmt"/>
+        <line num="87" count="1" type="stmt"/>
+        <line num="90" count="0" type="stmt"/>
+        <line num="93" count="0" type="stmt"/>
+        <line num="94" count="0" type="stmt"/>
+        <line num="107" count="0" type="stmt"/>
+        <line num="108" count="0" type="stmt"/>
+        <line num="121" count="0" type="stmt"/>
+        <line num="123" count="0" type="stmt"/>
+        <line num="128" count="0" type="stmt"/>
+        <line num="130" count="0" type="stmt"/>
+        <line num="131" count="0" type="stmt"/>
+        <line num="132" count="0" type="stmt"/>
+        <line num="134" count="0" type="stmt"/>
+        <line num="135" count="0" type="stmt"/>
+        <line num="136" count="0" type="stmt"/>
+        <line num="138" count="0" type="stmt"/>
+        <line num="139" count="0" type="stmt"/>
+        <line num="141" count="0" type="stmt"/>
+        <line num="142" count="0" type="stmt"/>
+        <line num="146" count="1" type="stmt"/>
+        <line num="147" count="0" type="stmt"/>
+        <line num="148" count="0" type="stmt"/>
+        <line num="149" count="0" type="stmt"/>
+        <line num="156" count="1" type="stmt"/>
+        <line num="157" count="0" type="stmt"/>
+        <line num="158" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="159" count="0" type="stmt"/>
+        <line num="160" count="0" type="stmt"/>
+        <line num="163" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="164" count="0" type="stmt"/>
+        <line num="165" count="0" type="stmt"/>
+        <line num="168" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="169" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="170" count="0" type="stmt"/>
+        <line num="171" count="0" type="stmt"/>
+        <line num="175" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="176" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="177" count="0" type="stmt"/>
+        <line num="178" count="0" type="stmt"/>
+        <line num="183" count="0" type="stmt"/>
+        <line num="184" count="0" type="stmt"/>
+        <line num="185" count="0" type="stmt"/>
+        <line num="187" count="0" type="stmt"/>
+        <line num="188" count="0" type="stmt"/>
+        <line num="189" count="0" type="stmt"/>
+        <line num="190" count="0" type="stmt"/>
+        <line num="192" count="0" type="stmt"/>
+        <line num="194" count="0" type="stmt"/>
+        <line num="195" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="196" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="197" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="198" count="0" type="stmt"/>
+        <line num="201" count="0" type="cond" truecount="0" falsecount="3"/>
+        <line num="202" count="0" type="stmt"/>
+        <line num="206" count="0" type="cond" truecount="0" falsecount="5"/>
+        <line num="207" count="0" type="stmt"/>
+        <line num="208" count="0" type="stmt"/>
+        <line num="209" count="0" type="stmt"/>
+        <line num="212" count="0" type="stmt"/>
+        <line num="216" count="1" type="stmt"/>
+        <line num="217" count="0" type="stmt"/>
+        <line num="218" count="0" type="stmt"/>
+        <line num="219" count="0" type="stmt"/>
+        <line num="220" count="0" type="stmt"/>
+        <line num="222" count="0" type="stmt"/>
+        <line num="223" count="0" type="stmt"/>
+        <line num="226" count="1" type="stmt"/>
+        <line num="227" count="0" type="stmt"/>
+        <line num="228" count="0" type="stmt"/>
+        <line num="229" count="0" type="stmt"/>
+        <line num="232" count="1" type="stmt"/>
+        <line num="233" count="0" type="stmt"/>
+        <line num="236" count="1" type="stmt"/>
+        <line num="237" count="0" type="stmt"/>
+        <line num="251" count="1" type="stmt"/>
+        <line num="252" count="0" type="stmt"/>
+        <line num="258" count="1" type="stmt"/>
+      </file>
+    </package>
+  </project>
+</coverage>

Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
qiniu-js/coverage/coverage-final.json


+ 224 - 0
qiniu-js/coverage/lcov-report/base.css

@@ -0,0 +1,224 @@
+body, html {
+  margin:0; padding: 0;
+  height: 100%;
+}
+body {
+    font-family: Helvetica Neue, Helvetica, Arial;
+    font-size: 14px;
+    color:#333;
+}
+.small { font-size: 12px; }
+*, *:after, *:before {
+  -webkit-box-sizing:border-box;
+     -moz-box-sizing:border-box;
+          box-sizing:border-box;
+  }
+h1 { font-size: 20px; margin: 0;}
+h2 { font-size: 14px; }
+pre {
+    font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
+    margin: 0;
+    padding: 0;
+    -moz-tab-size: 2;
+    -o-tab-size:  2;
+    tab-size: 2;
+}
+a { color:#0074D9; text-decoration:none; }
+a:hover { text-decoration:underline; }
+.strong { font-weight: bold; }
+.space-top1 { padding: 10px 0 0 0; }
+.pad2y { padding: 20px 0; }
+.pad1y { padding: 10px 0; }
+.pad2x { padding: 0 20px; }
+.pad2 { padding: 20px; }
+.pad1 { padding: 10px; }
+.space-left2 { padding-left:55px; }
+.space-right2 { padding-right:20px; }
+.center { text-align:center; }
+.clearfix { display:block; }
+.clearfix:after {
+  content:'';
+  display:block;
+  height:0;
+  clear:both;
+  visibility:hidden;
+  }
+.fl { float: left; }
+@media only screen and (max-width:640px) {
+  .col3 { width:100%; max-width:100%; }
+  .hide-mobile { display:none!important; }
+}
+
+.quiet {
+  color: #7f7f7f;
+  color: rgba(0,0,0,0.5);
+}
+.quiet a { opacity: 0.7; }
+
+.fraction {
+  font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
+  font-size: 10px;
+  color: #555;
+  background: #E8E8E8;
+  padding: 4px 5px;
+  border-radius: 3px;
+  vertical-align: middle;
+}
+
+div.path a:link, div.path a:visited { color: #333; }
+table.coverage {
+  border-collapse: collapse;
+  margin: 10px 0 0 0;
+  padding: 0;
+}
+
+table.coverage td {
+  margin: 0;
+  padding: 0;
+  vertical-align: top;
+}
+table.coverage td.line-count {
+    text-align: right;
+    padding: 0 5px 0 20px;
+}
+table.coverage td.line-coverage {
+    text-align: right;
+    padding-right: 10px;
+    min-width:20px;
+}
+
+table.coverage td span.cline-any {
+    display: inline-block;
+    padding: 0 5px;
+    width: 100%;
+}
+.missing-if-branch {
+    display: inline-block;
+    margin-right: 5px;
+    border-radius: 3px;
+    position: relative;
+    padding: 0 4px;
+    background: #333;
+    color: yellow;
+}
+
+.skip-if-branch {
+    display: none;
+    margin-right: 10px;
+    position: relative;
+    padding: 0 4px;
+    background: #ccc;
+    color: white;
+}
+.missing-if-branch .typ, .skip-if-branch .typ {
+    color: inherit !important;
+}
+.coverage-summary {
+  border-collapse: collapse;
+  width: 100%;
+}
+.coverage-summary tr { border-bottom: 1px solid #bbb; }
+.keyline-all { border: 1px solid #ddd; }
+.coverage-summary td, .coverage-summary th { padding: 10px; }
+.coverage-summary tbody { border: 1px solid #bbb; }
+.coverage-summary td { border-right: 1px solid #bbb; }
+.coverage-summary td:last-child { border-right: none; }
+.coverage-summary th {
+  text-align: left;
+  font-weight: normal;
+  white-space: nowrap;
+}
+.coverage-summary th.file { border-right: none !important; }
+.coverage-summary th.pct { }
+.coverage-summary th.pic,
+.coverage-summary th.abs,
+.coverage-summary td.pct,
+.coverage-summary td.abs { text-align: right; }
+.coverage-summary td.file { white-space: nowrap;  }
+.coverage-summary td.pic { min-width: 120px !important;  }
+.coverage-summary tfoot td { }
+
+.coverage-summary .sorter {
+    height: 10px;
+    width: 7px;
+    display: inline-block;
+    margin-left: 0.5em;
+    background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
+}
+.coverage-summary .sorted .sorter {
+    background-position: 0 -20px;
+}
+.coverage-summary .sorted-desc .sorter {
+    background-position: 0 -10px;
+}
+.status-line {  height: 10px; }
+/* yellow */
+.cbranch-no { background: yellow !important; color: #111; }
+/* dark red */
+.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
+.low .chart { border:1px solid #C21F39 }
+.highlighted,
+.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
+  background: #C21F39 !important;
+}
+/* medium red */
+.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
+/* light red */
+.low, .cline-no { background:#FCE1E5 }
+/* light green */
+.high, .cline-yes { background:rgb(230,245,208) }
+/* medium green */
+.cstat-yes { background:rgb(161,215,106) }
+/* dark green */
+.status-line.high, .high .cover-fill { background:rgb(77,146,33) }
+.high .chart { border:1px solid rgb(77,146,33) }
+/* dark yellow (gold) */
+.status-line.medium, .medium .cover-fill { background: #f9cd0b; }
+.medium .chart { border:1px solid #f9cd0b; }
+/* light yellow */
+.medium { background: #fff4c2; }
+
+.cstat-skip { background: #ddd; color: #111; }
+.fstat-skip { background: #ddd; color: #111 !important; }
+.cbranch-skip { background: #ddd !important; color: #111; }
+
+span.cline-neutral { background: #eaeaea; }
+
+.coverage-summary td.empty {
+    opacity: .5;
+    padding-top: 4px;
+    padding-bottom: 4px;
+    line-height: 1;
+    color: #888;
+}
+
+.cover-fill, .cover-empty {
+  display:inline-block;
+  height: 12px;
+}
+.chart {
+  line-height: 0;
+}
+.cover-empty {
+    background: white;
+}
+.cover-full {
+    border-right: none !important;
+}
+pre.prettyprint {
+    border: none !important;
+    padding: 0 !important;
+    margin: 0 !important;
+}
+.com { color: #999 !important; }
+.ignore-none { color: #999; font-weight: normal; }
+
+.wrapper {
+  min-height: 100%;
+  height: auto !important;
+  height: 100%;
+  margin: 0 auto -48px;
+}
+.footer, .push {
+  height: 48px;
+}

+ 79 - 0
qiniu-js/coverage/lcov-report/block-navigation.js

@@ -0,0 +1,79 @@
+/* eslint-disable */
+var jumpToCode = (function init() {
+    // Classes of code we would like to highlight in the file view
+    var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
+
+    // Elements to highlight in the file listing view
+    var fileListingElements = ['td.pct.low'];
+
+    // We don't want to select elements that are direct descendants of another match
+    var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
+
+    // Selecter that finds elements on the page to which we can jump
+    var selector =
+        fileListingElements.join(', ') +
+        ', ' +
+        notSelector +
+        missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
+
+    // The NodeList of matching elements
+    var missingCoverageElements = document.querySelectorAll(selector);
+
+    var currentIndex;
+
+    function toggleClass(index) {
+        missingCoverageElements
+            .item(currentIndex)
+            .classList.remove('highlighted');
+        missingCoverageElements.item(index).classList.add('highlighted');
+    }
+
+    function makeCurrent(index) {
+        toggleClass(index);
+        currentIndex = index;
+        missingCoverageElements.item(index).scrollIntoView({
+            behavior: 'smooth',
+            block: 'center',
+            inline: 'center'
+        });
+    }
+
+    function goToPrevious() {
+        var nextIndex = 0;
+        if (typeof currentIndex !== 'number' || currentIndex === 0) {
+            nextIndex = missingCoverageElements.length - 1;
+        } else if (missingCoverageElements.length > 1) {
+            nextIndex = currentIndex - 1;
+        }
+
+        makeCurrent(nextIndex);
+    }
+
+    function goToNext() {
+        var nextIndex = 0;
+
+        if (
+            typeof currentIndex === 'number' &&
+            currentIndex < missingCoverageElements.length - 1
+        ) {
+            nextIndex = currentIndex + 1;
+        }
+
+        makeCurrent(nextIndex);
+    }
+
+    return function jump(event) {
+        switch (event.which) {
+            case 78: // n
+            case 74: // j
+                goToNext();
+                break;
+            case 66: // b
+            case 75: // k
+            case 80: // p
+                goToPrevious();
+                break;
+        }
+    };
+})();
+window.addEventListener('keydown', jumpToCode);

BIN
qiniu-js/coverage/lcov-report/favicon.png


+ 141 - 0
qiniu-js/coverage/lcov-report/index.html

@@ -0,0 +1,141 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for All files</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="prettify.css" />
+    <link rel="stylesheet" href="base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1>All files</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">44.42% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>235/529</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">31.44% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>83/264</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">37.18% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>29/78</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">43.49% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>217/499</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line low'></div>
+    <div class="pad1">
+<table class="coverage-summary">
+<thead>
+<tr>
+   <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
+   <th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
+   <th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
+   <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
+   <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
+   <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
+   <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
+</tr>
+</thead>
+<tbody><tr>
+	<td class="file low" data-value="src"><a href="src/index.html">src</a></td>
+	<td data-value="44.64" class="pic low">
+	<div class="chart"><div class="cover-fill" style="width: 44%"></div><div class="cover-empty" style="width: 56%"></div></div>
+	</td>
+	<td data-value="44.64" class="pct low">44.64%</td>
+	<td data-value="401" class="abs low">179/401</td>
+	<td data-value="28.43" class="pct low">28.43%</td>
+	<td data-value="197" class="abs low">56/197</td>
+	<td data-value="33.33" class="pct low">33.33%</td>
+	<td data-value="60" class="abs low">20/60</td>
+	<td data-value="44.39" class="pct low">44.39%</td>
+	<td data-value="383" class="abs low">170/383</td>
+	</tr>
+
+<tr>
+	<td class="file high" data-value="src/logger"><a href="src/logger/index.html">src/logger</a></td>
+	<td data-value="97.73" class="pic high">
+	<div class="chart"><div class="cover-fill" style="width: 97%"></div><div class="cover-empty" style="width: 3%"></div></div>
+	</td>
+	<td data-value="97.73" class="pct high">97.73%</td>
+	<td data-value="44" class="abs high">43/44</td>
+	<td data-value="69.23" class="pct medium">69.23%</td>
+	<td data-value="39" class="abs medium">27/39</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="8" class="abs high">8/8</td>
+	<td data-value="97.14" class="pct high">97.14%</td>
+	<td data-value="35" class="abs high">34/35</td>
+	</tr>
+
+<tr>
+	<td class="file low" data-value="src/upload"><a href="src/upload/index.html">src/upload</a></td>
+	<td data-value="15.48" class="pic low">
+	<div class="chart"><div class="cover-fill" style="width: 15%"></div><div class="cover-empty" style="width: 85%"></div></div>
+	</td>
+	<td data-value="15.48" class="pct low">15.48%</td>
+	<td data-value="84" class="abs low">13/84</td>
+	<td data-value="0" class="pct low">0%</td>
+	<td data-value="28" class="abs low">0/28</td>
+	<td data-value="10" class="pct low">10%</td>
+	<td data-value="10" class="abs low">1/10</td>
+	<td data-value="16.05" class="pct low">16.05%</td>
+	<td data-value="81" class="abs low">13/81</td>
+	</tr>
+
+</tbody>
+</table>
+</div>
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Thu Apr 22 2021 01:38:54 GMT+0000 (Coordinated Universal Time)
+            </div>
+        </div>
+        <script src="prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="sorter.js"></script>
+        <script src="block-navigation.js"></script>
+    </body>
+</html>
+    

+ 1 - 0
qiniu-js/coverage/lcov-report/prettify.css

@@ -0,0 +1 @@
+.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}

Разлика између датотеке није приказан због своје велике величине
+ 1 - 0
qiniu-js/coverage/lcov-report/prettify.js


BIN
qiniu-js/coverage/lcov-report/sort-arrow-sprite.png


+ 170 - 0
qiniu-js/coverage/lcov-report/sorter.js

@@ -0,0 +1,170 @@
+/* eslint-disable */
+var addSorting = (function() {
+    'use strict';
+    var cols,
+        currentSort = {
+            index: 0,
+            desc: false
+        };
+
+    // returns the summary table element
+    function getTable() {
+        return document.querySelector('.coverage-summary');
+    }
+    // returns the thead element of the summary table
+    function getTableHeader() {
+        return getTable().querySelector('thead tr');
+    }
+    // returns the tbody element of the summary table
+    function getTableBody() {
+        return getTable().querySelector('tbody');
+    }
+    // returns the th element for nth column
+    function getNthColumn(n) {
+        return getTableHeader().querySelectorAll('th')[n];
+    }
+
+    // loads all columns
+    function loadColumns() {
+        var colNodes = getTableHeader().querySelectorAll('th'),
+            colNode,
+            cols = [],
+            col,
+            i;
+
+        for (i = 0; i < colNodes.length; i += 1) {
+            colNode = colNodes[i];
+            col = {
+                key: colNode.getAttribute('data-col'),
+                sortable: !colNode.getAttribute('data-nosort'),
+                type: colNode.getAttribute('data-type') || 'string'
+            };
+            cols.push(col);
+            if (col.sortable) {
+                col.defaultDescSort = col.type === 'number';
+                colNode.innerHTML =
+                    colNode.innerHTML + '<span class="sorter"></span>';
+            }
+        }
+        return cols;
+    }
+    // attaches a data attribute to every tr element with an object
+    // of data values keyed by column name
+    function loadRowData(tableRow) {
+        var tableCols = tableRow.querySelectorAll('td'),
+            colNode,
+            col,
+            data = {},
+            i,
+            val;
+        for (i = 0; i < tableCols.length; i += 1) {
+            colNode = tableCols[i];
+            col = cols[i];
+            val = colNode.getAttribute('data-value');
+            if (col.type === 'number') {
+                val = Number(val);
+            }
+            data[col.key] = val;
+        }
+        return data;
+    }
+    // loads all row data
+    function loadData() {
+        var rows = getTableBody().querySelectorAll('tr'),
+            i;
+
+        for (i = 0; i < rows.length; i += 1) {
+            rows[i].data = loadRowData(rows[i]);
+        }
+    }
+    // sorts the table using the data for the ith column
+    function sortByIndex(index, desc) {
+        var key = cols[index].key,
+            sorter = function(a, b) {
+                a = a.data[key];
+                b = b.data[key];
+                return a < b ? -1 : a > b ? 1 : 0;
+            },
+            finalSorter = sorter,
+            tableBody = document.querySelector('.coverage-summary tbody'),
+            rowNodes = tableBody.querySelectorAll('tr'),
+            rows = [],
+            i;
+
+        if (desc) {
+            finalSorter = function(a, b) {
+                return -1 * sorter(a, b);
+            };
+        }
+
+        for (i = 0; i < rowNodes.length; i += 1) {
+            rows.push(rowNodes[i]);
+            tableBody.removeChild(rowNodes[i]);
+        }
+
+        rows.sort(finalSorter);
+
+        for (i = 0; i < rows.length; i += 1) {
+            tableBody.appendChild(rows[i]);
+        }
+    }
+    // removes sort indicators for current column being sorted
+    function removeSortIndicators() {
+        var col = getNthColumn(currentSort.index),
+            cls = col.className;
+
+        cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
+        col.className = cls;
+    }
+    // adds sort indicators for current column being sorted
+    function addSortIndicators() {
+        getNthColumn(currentSort.index).className += currentSort.desc
+            ? ' sorted-desc'
+            : ' sorted';
+    }
+    // adds event listeners for all sorter widgets
+    function enableUI() {
+        var i,
+            el,
+            ithSorter = function ithSorter(i) {
+                var col = cols[i];
+
+                return function() {
+                    var desc = col.defaultDescSort;
+
+                    if (currentSort.index === i) {
+                        desc = !currentSort.desc;
+                    }
+                    sortByIndex(i, desc);
+                    removeSortIndicators();
+                    currentSort.index = i;
+                    currentSort.desc = desc;
+                    addSortIndicators();
+                };
+            };
+        for (i = 0; i < cols.length; i += 1) {
+            if (cols[i].sortable) {
+                // add the click event handler on the th so users
+                // dont have to click on those tiny arrows
+                el = getNthColumn(i).querySelector('.sorter').parentElement;
+                if (el.addEventListener) {
+                    el.addEventListener('click', ithSorter(i));
+                } else {
+                    el.attachEvent('onclick', ithSorter(i));
+                }
+            }
+        }
+    }
+    // adds sorting functionality to the UI
+    return function() {
+        if (!getTable()) {
+            return;
+        }
+        cols = loadColumns();
+        loadData();
+        addSortIndicators();
+        enableUI();
+    };
+})();
+
+window.addEventListener('load', addSorting);

+ 533 - 0
qiniu-js/coverage/lcov-report/src/api.ts.html

@@ -0,0 +1,533 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for src/api.ts</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../prettify.css" />
+    <link rel="stylesheet" href="../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../index.html">All files</a> / <a href="index.html">src</a> api.ts</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">63.89% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>23/36</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">50% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>6/12</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">28.57% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>2/7</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">62.86% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>22/35</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line medium'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a>
+<a name='L17'></a><a href='#L17'>17</a>
+<a name='L18'></a><a href='#L18'>18</a>
+<a name='L19'></a><a href='#L19'>19</a>
+<a name='L20'></a><a href='#L20'>20</a>
+<a name='L21'></a><a href='#L21'>21</a>
+<a name='L22'></a><a href='#L22'>22</a>
+<a name='L23'></a><a href='#L23'>23</a>
+<a name='L24'></a><a href='#L24'>24</a>
+<a name='L25'></a><a href='#L25'>25</a>
+<a name='L26'></a><a href='#L26'>26</a>
+<a name='L27'></a><a href='#L27'>27</a>
+<a name='L28'></a><a href='#L28'>28</a>
+<a name='L29'></a><a href='#L29'>29</a>
+<a name='L30'></a><a href='#L30'>30</a>
+<a name='L31'></a><a href='#L31'>31</a>
+<a name='L32'></a><a href='#L32'>32</a>
+<a name='L33'></a><a href='#L33'>33</a>
+<a name='L34'></a><a href='#L34'>34</a>
+<a name='L35'></a><a href='#L35'>35</a>
+<a name='L36'></a><a href='#L36'>36</a>
+<a name='L37'></a><a href='#L37'>37</a>
+<a name='L38'></a><a href='#L38'>38</a>
+<a name='L39'></a><a href='#L39'>39</a>
+<a name='L40'></a><a href='#L40'>40</a>
+<a name='L41'></a><a href='#L41'>41</a>
+<a name='L42'></a><a href='#L42'>42</a>
+<a name='L43'></a><a href='#L43'>43</a>
+<a name='L44'></a><a href='#L44'>44</a>
+<a name='L45'></a><a href='#L45'>45</a>
+<a name='L46'></a><a href='#L46'>46</a>
+<a name='L47'></a><a href='#L47'>47</a>
+<a name='L48'></a><a href='#L48'>48</a>
+<a name='L49'></a><a href='#L49'>49</a>
+<a name='L50'></a><a href='#L50'>50</a>
+<a name='L51'></a><a href='#L51'>51</a>
+<a name='L52'></a><a href='#L52'>52</a>
+<a name='L53'></a><a href='#L53'>53</a>
+<a name='L54'></a><a href='#L54'>54</a>
+<a name='L55'></a><a href='#L55'>55</a>
+<a name='L56'></a><a href='#L56'>56</a>
+<a name='L57'></a><a href='#L57'>57</a>
+<a name='L58'></a><a href='#L58'>58</a>
+<a name='L59'></a><a href='#L59'>59</a>
+<a name='L60'></a><a href='#L60'>60</a>
+<a name='L61'></a><a href='#L61'>61</a>
+<a name='L62'></a><a href='#L62'>62</a>
+<a name='L63'></a><a href='#L63'>63</a>
+<a name='L64'></a><a href='#L64'>64</a>
+<a name='L65'></a><a href='#L65'>65</a>
+<a name='L66'></a><a href='#L66'>66</a>
+<a name='L67'></a><a href='#L67'>67</a>
+<a name='L68'></a><a href='#L68'>68</a>
+<a name='L69'></a><a href='#L69'>69</a>
+<a name='L70'></a><a href='#L70'>70</a>
+<a name='L71'></a><a href='#L71'>71</a>
+<a name='L72'></a><a href='#L72'>72</a>
+<a name='L73'></a><a href='#L73'>73</a>
+<a name='L74'></a><a href='#L74'>74</a>
+<a name='L75'></a><a href='#L75'>75</a>
+<a name='L76'></a><a href='#L76'>76</a>
+<a name='L77'></a><a href='#L77'>77</a>
+<a name='L78'></a><a href='#L78'>78</a>
+<a name='L79'></a><a href='#L79'>79</a>
+<a name='L80'></a><a href='#L80'>80</a>
+<a name='L81'></a><a href='#L81'>81</a>
+<a name='L82'></a><a href='#L82'>82</a>
+<a name='L83'></a><a href='#L83'>83</a>
+<a name='L84'></a><a href='#L84'>84</a>
+<a name='L85'></a><a href='#L85'>85</a>
+<a name='L86'></a><a href='#L86'>86</a>
+<a name='L87'></a><a href='#L87'>87</a>
+<a name='L88'></a><a href='#L88'>88</a>
+<a name='L89'></a><a href='#L89'>89</a>
+<a name='L90'></a><a href='#L90'>90</a>
+<a name='L91'></a><a href='#L91'>91</a>
+<a name='L92'></a><a href='#L92'>92</a>
+<a name='L93'></a><a href='#L93'>93</a>
+<a name='L94'></a><a href='#L94'>94</a>
+<a name='L95'></a><a href='#L95'>95</a>
+<a name='L96'></a><a href='#L96'>96</a>
+<a name='L97'></a><a href='#L97'>97</a>
+<a name='L98'></a><a href='#L98'>98</a>
+<a name='L99'></a><a href='#L99'>99</a>
+<a name='L100'></a><a href='#L100'>100</a>
+<a name='L101'></a><a href='#L101'>101</a>
+<a name='L102'></a><a href='#L102'>102</a>
+<a name='L103'></a><a href='#L103'>103</a>
+<a name='L104'></a><a href='#L104'>104</a>
+<a name='L105'></a><a href='#L105'>105</a>
+<a name='L106'></a><a href='#L106'>106</a>
+<a name='L107'></a><a href='#L107'>107</a>
+<a name='L108'></a><a href='#L108'>108</a>
+<a name='L109'></a><a href='#L109'>109</a>
+<a name='L110'></a><a href='#L110'>110</a>
+<a name='L111'></a><a href='#L111'>111</a>
+<a name='L112'></a><a href='#L112'>112</a>
+<a name='L113'></a><a href='#L113'>113</a>
+<a name='L114'></a><a href='#L114'>114</a>
+<a name='L115'></a><a href='#L115'>115</a>
+<a name='L116'></a><a href='#L116'>116</a>
+<a name='L117'></a><a href='#L117'>117</a>
+<a name='L118'></a><a href='#L118'>118</a>
+<a name='L119'></a><a href='#L119'>119</a>
+<a name='L120'></a><a href='#L120'>120</a>
+<a name='L121'></a><a href='#L121'>121</a>
+<a name='L122'></a><a href='#L122'>122</a>
+<a name='L123'></a><a href='#L123'>123</a>
+<a name='L124'></a><a href='#L124'>124</a>
+<a name='L125'></a><a href='#L125'>125</a>
+<a name='L126'></a><a href='#L126'>126</a>
+<a name='L127'></a><a href='#L127'>127</a>
+<a name='L128'></a><a href='#L128'>128</a>
+<a name='L129'></a><a href='#L129'>129</a>
+<a name='L130'></a><a href='#L130'>130</a>
+<a name='L131'></a><a href='#L131'>131</a>
+<a name='L132'></a><a href='#L132'>132</a>
+<a name='L133'></a><a href='#L133'>133</a>
+<a name='L134'></a><a href='#L134'>134</a>
+<a name='L135'></a><a href='#L135'>135</a>
+<a name='L136'></a><a href='#L136'>136</a>
+<a name='L137'></a><a href='#L137'>137</a>
+<a name='L138'></a><a href='#L138'>138</a>
+<a name='L139'></a><a href='#L139'>139</a>
+<a name='L140'></a><a href='#L140'>140</a>
+<a name='L141'></a><a href='#L141'>141</a>
+<a name='L142'></a><a href='#L142'>142</a>
+<a name='L143'></a><a href='#L143'>143</a>
+<a name='L144'></a><a href='#L144'>144</a>
+<a name='L145'></a><a href='#L145'>145</a>
+<a name='L146'></a><a href='#L146'>146</a>
+<a name='L147'></a><a href='#L147'>147</a>
+<a name='L148'></a><a href='#L148'>148</a>
+<a name='L149'></a><a href='#L149'>149</a>
+<a name='L150'></a><a href='#L150'>150</a>
+<a name='L151'></a><a href='#L151'>151</a>
+<a name='L152'></a><a href='#L152'>152</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">5x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">5x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">4x</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import * as utils from './utils'
+import { regionUphostMap } from './config'
+import { urlSafeBase64Encode } from './base64'
+import { Config, UploadInfo } from './upload'
+&nbsp;
+interface UpHosts {
+  data: {
+    up: {
+      acc: {
+        main: string[]
+      }
+    }
+  }
+}
+&nbsp;
+export async function getUpHosts(token: string, protocol: 'https:' | 'http:'): Promise&lt;UpHosts&gt; {
+  const putPolicy = utils.getPutPolicy(token)
+  const url = protocol + '//api.qiniu.com/v2/query?ak=' + putPolicy.ak + '&amp;bucket=' + putPolicy.bucket
+  return utils.request(url, { method: 'GET' })
+}
+&nbsp;
+export type UploadUrlConfig = Partial&lt;Pick&lt;Config, 'upprotocol' | 'uphost' | 'region' | 'useCdnDomain'&gt;&gt;
+&nbsp;
+/** 获取上传url */
+export async function getUploadUrl(config: UploadUrlConfig, token: string): Promise&lt;string&gt; {
+  const protocol = config.upprotocol || <span class="branch-1 cbranch-no" title="branch not covered" >'https:'</span>
+&nbsp;
+  if (config.uphost) {
+    return `${protocol}//${config.uphost}`
+  }
+&nbsp;
+  if (config.region) {
+    const upHosts = regionUphostMap[config.region]
+    const host = config.useCdnDomain ? upHosts.cdnUphost : <span class="branch-1 cbranch-no" title="branch not covered" >upHosts.srcUphost</span>
+    return `${protocol}//${host}`
+  }
+&nbsp;
+  const res = await getUpHosts(token, protocol)
+  const hosts = res.data.up.acc.main
+  return `${protocol}//${hosts[0]}`
+}
+&nbsp;
+/**
+ * @param bucket 空间名
+ * @param key 目标文件名
+ * @param uploadInfo 上传信息
+ */
+function <span class="fstat-no" title="function not covered" >getBaseUrl(</span>bucket: string, key: string | null | undefined, uploadInfo: UploadInfo) {
+  const { url, id } = <span class="cstat-no" title="statement not covered" >uploadInfo</span>
+<span class="cstat-no" title="statement not covered" >  return `${url}/buckets/${bucket}/objects/${key != null ? urlSafeBase64Encode(key) : '~'}/uploads/${id}`</span>
+}
+&nbsp;
+export interface InitPartsData {
+  /** 该文件的上传 id, 后续该文件其他各个块的上传,已上传块的废弃,已上传块的合成文件,都需要该 id */
+  uploadId: string
+  /** uploadId 的过期时间 */
+  expireAt: number
+}
+&nbsp;
+/**
+ * @param token 上传鉴权凭证
+ * @param bucket 上传空间
+ * @param key 目标文件名
+ * @param uploadUrl 上传地址
+ */
+export function <span class="fstat-no" title="function not covered" >initUploadParts(</span>
+  token: string,
+  bucket: string,
+  key: string | null | undefined,
+  uploadUrl: string
+): utils.Response&lt;InitPartsData&gt; {
+  const url = `${<span class="cstat-no" title="statement not covered" >uploadUrl}/buckets/${bucket}/objects/${key != null ? urlSafeBase64Encode(key) : '~'}/uploads`</span>
+<span class="cstat-no" title="statement not covered" >  return utils.request&lt;InitPartsData&gt;(</span>
+    url,
+    {
+      method: 'POST',
+      headers: utils.getAuthHeaders(token)
+    }
+  )
+}
+&nbsp;
+export interface UploadChunkData {
+  etag: string
+  md5: string
+}
+&nbsp;
+/**
+ * @param token 上传鉴权凭证
+ * @param index 当前 chunk 的索引
+ * @param uploadInfo 上传信息
+ * @param options 请求参数
+ */
+export function <span class="fstat-no" title="function not covered" >uploadChunk(</span>
+  token: string,
+  key: string | null | undefined,
+  index: number,
+  uploadInfo: UploadInfo,
+  options: Partial&lt;utils.RequestOptions&gt;
+): utils.Response&lt;UploadChunkData&gt; {
+  const bucket = <span class="cstat-no" title="statement not covered" >utils.getPutPolicy(token).bucket</span>
+  const url = <span class="cstat-no" title="statement not covered" >getBaseUrl(bucket, key, uploadInfo) + `/${index}`</span>
+<span class="cstat-no" title="statement not covered" >  return utils.request&lt;UploadChunkData&gt;(url, {</span>
+    ...options,
+    method: 'PUT',
+    headers: utils.getHeadersForChunkUpload(token)
+  })
+}
+&nbsp;
+export type UploadCompleteData = any
+&nbsp;
+/**
+ * @param token 上传鉴权凭证
+ * @param key 目标文件名
+ * @param uploadInfo 上传信息
+ * @param options 请求参数
+ */
+export function <span class="fstat-no" title="function not covered" >uploadComplete(</span>
+  token: string,
+  key: string | null | undefined,
+  uploadInfo: UploadInfo,
+  options: Partial&lt;utils.RequestOptions&gt;
+): utils.Response&lt;UploadCompleteData&gt; {
+  const bucket = <span class="cstat-no" title="statement not covered" >utils.getPutPolicy(token).bucket</span>
+  const url = <span class="cstat-no" title="statement not covered" >getBaseUrl(bucket, key, uploadInfo)</span>
+<span class="cstat-no" title="statement not covered" >  return utils.request&lt;UploadCompleteData&gt;(url, {</span>
+    ...options,
+    method: 'POST',
+    headers: utils.getHeadersForMkFile(token)
+  })
+}
+&nbsp;
+/**
+ * @param token 上传鉴权凭证
+ * @param key 目标文件名
+ * @param uploadInfo 上传信息
+ */
+export function <span class="fstat-no" title="function not covered" >deleteUploadedChunks(</span>
+  token: string,
+  key: string | null | undefined,
+  uploadinfo: UploadInfo
+): utils.Response&lt;void&gt; {
+  const bucket = <span class="cstat-no" title="statement not covered" >utils.getPutPolicy(token).bucket</span>
+  const url = <span class="cstat-no" title="statement not covered" >getBaseUrl(bucket, key, uploadinfo)</span>
+<span class="cstat-no" title="statement not covered" >  return utils.request(</span>
+    url,
+    {
+      method: 'DELETE',
+      headers: utils.getAuthHeaders(token)
+    }
+  )
+}
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Thu Apr 22 2021 01:38:54 GMT+0000 (Coordinated Universal Time)
+            </div>
+        </div>
+        <script src="../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../sorter.js"></script>
+        <script src="../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 710 - 0
qiniu-js/coverage/lcov-report/src/base64.ts.html

@@ -0,0 +1,710 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for src/base64.ts</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../prettify.css" />
+    <link rel="stylesheet" href="../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../index.html">All files</a> / <a href="index.html">src</a> base64.ts</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">50% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>45/90</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">25% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>8/32</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">60% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>3/5</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">49.44% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>44/89</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line medium'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a>
+<a name='L17'></a><a href='#L17'>17</a>
+<a name='L18'></a><a href='#L18'>18</a>
+<a name='L19'></a><a href='#L19'>19</a>
+<a name='L20'></a><a href='#L20'>20</a>
+<a name='L21'></a><a href='#L21'>21</a>
+<a name='L22'></a><a href='#L22'>22</a>
+<a name='L23'></a><a href='#L23'>23</a>
+<a name='L24'></a><a href='#L24'>24</a>
+<a name='L25'></a><a href='#L25'>25</a>
+<a name='L26'></a><a href='#L26'>26</a>
+<a name='L27'></a><a href='#L27'>27</a>
+<a name='L28'></a><a href='#L28'>28</a>
+<a name='L29'></a><a href='#L29'>29</a>
+<a name='L30'></a><a href='#L30'>30</a>
+<a name='L31'></a><a href='#L31'>31</a>
+<a name='L32'></a><a href='#L32'>32</a>
+<a name='L33'></a><a href='#L33'>33</a>
+<a name='L34'></a><a href='#L34'>34</a>
+<a name='L35'></a><a href='#L35'>35</a>
+<a name='L36'></a><a href='#L36'>36</a>
+<a name='L37'></a><a href='#L37'>37</a>
+<a name='L38'></a><a href='#L38'>38</a>
+<a name='L39'></a><a href='#L39'>39</a>
+<a name='L40'></a><a href='#L40'>40</a>
+<a name='L41'></a><a href='#L41'>41</a>
+<a name='L42'></a><a href='#L42'>42</a>
+<a name='L43'></a><a href='#L43'>43</a>
+<a name='L44'></a><a href='#L44'>44</a>
+<a name='L45'></a><a href='#L45'>45</a>
+<a name='L46'></a><a href='#L46'>46</a>
+<a name='L47'></a><a href='#L47'>47</a>
+<a name='L48'></a><a href='#L48'>48</a>
+<a name='L49'></a><a href='#L49'>49</a>
+<a name='L50'></a><a href='#L50'>50</a>
+<a name='L51'></a><a href='#L51'>51</a>
+<a name='L52'></a><a href='#L52'>52</a>
+<a name='L53'></a><a href='#L53'>53</a>
+<a name='L54'></a><a href='#L54'>54</a>
+<a name='L55'></a><a href='#L55'>55</a>
+<a name='L56'></a><a href='#L56'>56</a>
+<a name='L57'></a><a href='#L57'>57</a>
+<a name='L58'></a><a href='#L58'>58</a>
+<a name='L59'></a><a href='#L59'>59</a>
+<a name='L60'></a><a href='#L60'>60</a>
+<a name='L61'></a><a href='#L61'>61</a>
+<a name='L62'></a><a href='#L62'>62</a>
+<a name='L63'></a><a href='#L63'>63</a>
+<a name='L64'></a><a href='#L64'>64</a>
+<a name='L65'></a><a href='#L65'>65</a>
+<a name='L66'></a><a href='#L66'>66</a>
+<a name='L67'></a><a href='#L67'>67</a>
+<a name='L68'></a><a href='#L68'>68</a>
+<a name='L69'></a><a href='#L69'>69</a>
+<a name='L70'></a><a href='#L70'>70</a>
+<a name='L71'></a><a href='#L71'>71</a>
+<a name='L72'></a><a href='#L72'>72</a>
+<a name='L73'></a><a href='#L73'>73</a>
+<a name='L74'></a><a href='#L74'>74</a>
+<a name='L75'></a><a href='#L75'>75</a>
+<a name='L76'></a><a href='#L76'>76</a>
+<a name='L77'></a><a href='#L77'>77</a>
+<a name='L78'></a><a href='#L78'>78</a>
+<a name='L79'></a><a href='#L79'>79</a>
+<a name='L80'></a><a href='#L80'>80</a>
+<a name='L81'></a><a href='#L81'>81</a>
+<a name='L82'></a><a href='#L82'>82</a>
+<a name='L83'></a><a href='#L83'>83</a>
+<a name='L84'></a><a href='#L84'>84</a>
+<a name='L85'></a><a href='#L85'>85</a>
+<a name='L86'></a><a href='#L86'>86</a>
+<a name='L87'></a><a href='#L87'>87</a>
+<a name='L88'></a><a href='#L88'>88</a>
+<a name='L89'></a><a href='#L89'>89</a>
+<a name='L90'></a><a href='#L90'>90</a>
+<a name='L91'></a><a href='#L91'>91</a>
+<a name='L92'></a><a href='#L92'>92</a>
+<a name='L93'></a><a href='#L93'>93</a>
+<a name='L94'></a><a href='#L94'>94</a>
+<a name='L95'></a><a href='#L95'>95</a>
+<a name='L96'></a><a href='#L96'>96</a>
+<a name='L97'></a><a href='#L97'>97</a>
+<a name='L98'></a><a href='#L98'>98</a>
+<a name='L99'></a><a href='#L99'>99</a>
+<a name='L100'></a><a href='#L100'>100</a>
+<a name='L101'></a><a href='#L101'>101</a>
+<a name='L102'></a><a href='#L102'>102</a>
+<a name='L103'></a><a href='#L103'>103</a>
+<a name='L104'></a><a href='#L104'>104</a>
+<a name='L105'></a><a href='#L105'>105</a>
+<a name='L106'></a><a href='#L106'>106</a>
+<a name='L107'></a><a href='#L107'>107</a>
+<a name='L108'></a><a href='#L108'>108</a>
+<a name='L109'></a><a href='#L109'>109</a>
+<a name='L110'></a><a href='#L110'>110</a>
+<a name='L111'></a><a href='#L111'>111</a>
+<a name='L112'></a><a href='#L112'>112</a>
+<a name='L113'></a><a href='#L113'>113</a>
+<a name='L114'></a><a href='#L114'>114</a>
+<a name='L115'></a><a href='#L115'>115</a>
+<a name='L116'></a><a href='#L116'>116</a>
+<a name='L117'></a><a href='#L117'>117</a>
+<a name='L118'></a><a href='#L118'>118</a>
+<a name='L119'></a><a href='#L119'>119</a>
+<a name='L120'></a><a href='#L120'>120</a>
+<a name='L121'></a><a href='#L121'>121</a>
+<a name='L122'></a><a href='#L122'>122</a>
+<a name='L123'></a><a href='#L123'>123</a>
+<a name='L124'></a><a href='#L124'>124</a>
+<a name='L125'></a><a href='#L125'>125</a>
+<a name='L126'></a><a href='#L126'>126</a>
+<a name='L127'></a><a href='#L127'>127</a>
+<a name='L128'></a><a href='#L128'>128</a>
+<a name='L129'></a><a href='#L129'>129</a>
+<a name='L130'></a><a href='#L130'>130</a>
+<a name='L131'></a><a href='#L131'>131</a>
+<a name='L132'></a><a href='#L132'>132</a>
+<a name='L133'></a><a href='#L133'>133</a>
+<a name='L134'></a><a href='#L134'>134</a>
+<a name='L135'></a><a href='#L135'>135</a>
+<a name='L136'></a><a href='#L136'>136</a>
+<a name='L137'></a><a href='#L137'>137</a>
+<a name='L138'></a><a href='#L138'>138</a>
+<a name='L139'></a><a href='#L139'>139</a>
+<a name='L140'></a><a href='#L140'>140</a>
+<a name='L141'></a><a href='#L141'>141</a>
+<a name='L142'></a><a href='#L142'>142</a>
+<a name='L143'></a><a href='#L143'>143</a>
+<a name='L144'></a><a href='#L144'>144</a>
+<a name='L145'></a><a href='#L145'>145</a>
+<a name='L146'></a><a href='#L146'>146</a>
+<a name='L147'></a><a href='#L147'>147</a>
+<a name='L148'></a><a href='#L148'>148</a>
+<a name='L149'></a><a href='#L149'>149</a>
+<a name='L150'></a><a href='#L150'>150</a>
+<a name='L151'></a><a href='#L151'>151</a>
+<a name='L152'></a><a href='#L152'>152</a>
+<a name='L153'></a><a href='#L153'>153</a>
+<a name='L154'></a><a href='#L154'>154</a>
+<a name='L155'></a><a href='#L155'>155</a>
+<a name='L156'></a><a href='#L156'>156</a>
+<a name='L157'></a><a href='#L157'>157</a>
+<a name='L158'></a><a href='#L158'>158</a>
+<a name='L159'></a><a href='#L159'>159</a>
+<a name='L160'></a><a href='#L160'>160</a>
+<a name='L161'></a><a href='#L161'>161</a>
+<a name='L162'></a><a href='#L162'>162</a>
+<a name='L163'></a><a href='#L163'>163</a>
+<a name='L164'></a><a href='#L164'>164</a>
+<a name='L165'></a><a href='#L165'>165</a>
+<a name='L166'></a><a href='#L166'>166</a>
+<a name='L167'></a><a href='#L167'>167</a>
+<a name='L168'></a><a href='#L168'>168</a>
+<a name='L169'></a><a href='#L169'>169</a>
+<a name='L170'></a><a href='#L170'>170</a>
+<a name='L171'></a><a href='#L171'>171</a>
+<a name='L172'></a><a href='#L172'>172</a>
+<a name='L173'></a><a href='#L173'>173</a>
+<a name='L174'></a><a href='#L174'>174</a>
+<a name='L175'></a><a href='#L175'>175</a>
+<a name='L176'></a><a href='#L176'>176</a>
+<a name='L177'></a><a href='#L177'>177</a>
+<a name='L178'></a><a href='#L178'>178</a>
+<a name='L179'></a><a href='#L179'>179</a>
+<a name='L180'></a><a href='#L180'>180</a>
+<a name='L181'></a><a href='#L181'>181</a>
+<a name='L182'></a><a href='#L182'>182</a>
+<a name='L183'></a><a href='#L183'>183</a>
+<a name='L184'></a><a href='#L184'>184</a>
+<a name='L185'></a><a href='#L185'>185</a>
+<a name='L186'></a><a href='#L186'>186</a>
+<a name='L187'></a><a href='#L187'>187</a>
+<a name='L188'></a><a href='#L188'>188</a>
+<a name='L189'></a><a href='#L189'>189</a>
+<a name='L190'></a><a href='#L190'>190</a>
+<a name='L191'></a><a href='#L191'>191</a>
+<a name='L192'></a><a href='#L192'>192</a>
+<a name='L193'></a><a href='#L193'>193</a>
+<a name='L194'></a><a href='#L194'>194</a>
+<a name='L195'></a><a href='#L195'>195</a>
+<a name='L196'></a><a href='#L196'>196</a>
+<a name='L197'></a><a href='#L197'>197</a>
+<a name='L198'></a><a href='#L198'>198</a>
+<a name='L199'></a><a href='#L199'>199</a>
+<a name='L200'></a><a href='#L200'>200</a>
+<a name='L201'></a><a href='#L201'>201</a>
+<a name='L202'></a><a href='#L202'>202</a>
+<a name='L203'></a><a href='#L203'>203</a>
+<a name='L204'></a><a href='#L204'>204</a>
+<a name='L205'></a><a href='#L205'>205</a>
+<a name='L206'></a><a href='#L206'>206</a>
+<a name='L207'></a><a href='#L207'>207</a>
+<a name='L208'></a><a href='#L208'>208</a>
+<a name='L209'></a><a href='#L209'>209</a>
+<a name='L210'></a><a href='#L210'>210</a>
+<a name='L211'></a><a href='#L211'>211</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">86x</span>
+<span class="cline-any cline-yes">86x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">86x</span>
+<span class="cline-any cline-yes">86x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">86x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">30x</span>
+<span class="cline-any cline-yes">30x</span>
+<span class="cline-any cline-yes">30x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">30x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">30x</span>
+<span class="cline-any cline-yes">30x</span>
+<span class="cline-any cline-yes">30x</span>
+<span class="cline-any cline-yes">30x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">30x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">/* eslint-disable */
+&nbsp;
+export function utf8Encode(argString: any) {
+  // http://kevin.vanzonneveld.net
+  // +   original by: Webtoolkit.info (http://www.webtoolkit.info/)
+  // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+  // +   improved by: sowberry
+  // +    tweaked by: Jack
+  // +   bugfixed by: Onno Marsman
+  // +   improved by: Yves Sucaet
+  // +   bugfixed by: Onno Marsman
+  // +   bugfixed by: Ulrich
+  // +   bugfixed by: Rafal Kukawski
+  // +   improved by: kirilloid
+  // +   bugfixed by: kirilloid
+  // *     example 1: this.utf8Encode('Kevin van Zonneveld')
+  // *     returns 1: 'Kevin van Zonneveld'
+&nbsp;
+  <span class="missing-if-branch" title="if path not taken" >I</span>if (argString === null || typeof argString === 'undefined') {
+<span class="cstat-no" title="statement not covered" >    return ''</span>
+  }
+&nbsp;
+  let string = argString + '' // .replace(/\r\n/g, '\n').replace(/\r/g, '\n')
+  let utftext = '',
+    start,
+    end,
+    stringl = 0
+&nbsp;
+  start = end = 0
+  stringl = string.length
+  for (let n = 0; n &lt; stringl; n++) {
+    let c1 = string.charCodeAt(n)
+    let enc = null
+&nbsp;
+    <span class="missing-if-branch" title="else path not taken" >E</span>if (c1 &lt; 128) {
+      end++
+    } else <span class="cstat-no" title="statement not covered" >if (c1 &gt; 127 &amp;&amp; c1 &lt; 2048) {</span>
+<span class="cstat-no" title="statement not covered" >      enc = String.fromCharCode((c1 &gt;&gt; 6) | 192, (c1 &amp; 63) | 128)</span>
+    } else <span class="cstat-no" title="statement not covered" >if ((c1 &amp; 0xf800 ^ 0xd800) &gt; 0) {</span>
+<span class="cstat-no" title="statement not covered" >      enc = String.fromCharCode(</span>
+        (c1 &gt;&gt; 12) | 224,
+        ((c1 &gt;&gt; 6) &amp; 63) | 128,
+        (c1 &amp; 63) | 128
+      )
+    } else {
+      // surrogate pairs
+<span class="cstat-no" title="statement not covered" >      if ((c1 &amp; 0xfc00 ^ 0xd800) &gt; 0) {</span>
+<span class="cstat-no" title="statement not covered" >        throw new RangeError('Unmatched trail surrogate at ' + n)</span>
+      }
+      let c2 = <span class="cstat-no" title="statement not covered" >string.charCodeAt(++n)</span>
+<span class="cstat-no" title="statement not covered" >      if ((c2 &amp; 0xfc00 ^ 0xdc00) &gt; 0) {</span>
+<span class="cstat-no" title="statement not covered" >        throw new RangeError('Unmatched lead surrogate at ' + (n - 1))</span>
+      }
+<span class="cstat-no" title="statement not covered" >      c1 = ((c1 &amp; 0x3ff) &lt;&lt; 10) + (c2 &amp; 0x3ff) + 0x10000</span>
+<span class="cstat-no" title="statement not covered" >      enc = String.fromCharCode(</span>
+        (c1 &gt;&gt; 18) | 240,
+        ((c1 &gt;&gt; 12) &amp; 63) | 128,
+        ((c1 &gt;&gt; 6) &amp; 63) | 128,
+        (c1 &amp; 63) | 128
+      )
+    }
+    <span class="missing-if-branch" title="if path not taken" >I</span>if (enc !== null) {
+<span class="cstat-no" title="statement not covered" >      if (end &gt; start) {</span>
+<span class="cstat-no" title="statement not covered" >        utftext += string.slice(start, end)</span>
+      }
+<span class="cstat-no" title="statement not covered" >      utftext += enc</span>
+<span class="cstat-no" title="statement not covered" >      start = end = n + 1</span>
+    }
+  }
+&nbsp;
+  <span class="missing-if-branch" title="else path not taken" >E</span>if (end &gt; start) {
+    utftext += string.slice(start, stringl)
+  }
+&nbsp;
+  return utftext
+}
+&nbsp;
+export function base64Encode(data: any) {
+  // http://kevin.vanzonneveld.net
+  // +   original by: Tyler Akins (http://rumkin.com)
+  // +   improved by: Bayron Guevara
+  // +   improved by: Thunder.m
+  // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+  // +   bugfixed by: Pellentesque Malesuada
+  // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+  // -    depends on: this.utf8Encode
+  // *     example 1: this.base64Encode('Kevin van Zonneveld')
+  // *     returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
+  // mozilla has this native
+  // - but breaks in 2.0.0.12!
+  // if (typeof this.window['atob'] == 'function') {
+  //    return atob(data)
+  // }
+  let b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
+  let o1,
+    o2,
+    o3,
+    h1,
+    h2,
+    h3,
+    h4,
+    bits,
+    i = 0,
+    ac = 0,
+    enc = '',
+    tmp_arr = []
+&nbsp;
+  <span class="missing-if-branch" title="if path not taken" >I</span>if (!data) {
+<span class="cstat-no" title="statement not covered" >    return data</span>
+  }
+&nbsp;
+  data = utf8Encode(data + '')
+&nbsp;
+  do {
+    // pack three octets into four hexets
+    o1 = data.charCodeAt(i++)
+    o2 = data.charCodeAt(i++)
+    o3 = data.charCodeAt(i++)
+&nbsp;
+    bits = (o1 &lt;&lt; 16) | (o2 &lt;&lt; 8) | o3
+&nbsp;
+    h1 = (bits &gt;&gt; 18) &amp; 0x3f
+    h2 = (bits &gt;&gt; 12) &amp; 0x3f
+    h3 = (bits &gt;&gt; 6) &amp; 0x3f
+    h4 = bits &amp; 0x3f
+&nbsp;
+    // use hexets to index into b64, and append result to encoded string
+    tmp_arr[ac++] =
+      b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4)
+  } while (i &lt; data.length)
+&nbsp;
+  enc = tmp_arr.join('')
+&nbsp;
+  switch (data.length % 3) {
+    case 1:
+      enc = enc.slice(0, -2) + '=='
+      break
+<span class="branch-1 cbranch-no" title="branch not covered" >    case 2:</span>
+<span class="cstat-no" title="statement not covered" >      enc = enc.slice(0, -1) + '='</span>
+<span class="cstat-no" title="statement not covered" >      break</span>
+  }
+&nbsp;
+  return enc
+}
+&nbsp;
+export function <span class="fstat-no" title="function not covered" >base64Decode(</span>data: string) {
+  // http://kevin.vanzonneveld.net
+  // +   original by: Tyler Akins (http://rumkin.com)
+  // +   improved by: Thunder.m
+  // +      input by: Aman Gupta
+  // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+  // +   bugfixed by: Onno Marsman
+  // +   bugfixed by: Pellentesque Malesuada
+  // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+  // +      input by: Brett Zamir (http://brett-zamir.me)
+  // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+  // *     example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==')
+  // *     returns 1: 'Kevin van Zonneveld'
+  // mozilla has this native
+  // - but breaks in 2.0.0.12!
+  // if (typeof this.window['atob'] == 'function') {
+  //    return atob(data)
+  // }
+  let b64 = <span class="cstat-no" title="statement not covered" >'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='</span>
+  let o1, o2, o3, h1, h2, h3, h4, bits, i = <span class="cstat-no" title="statement not covered" >0,</span>
+      ac = <span class="cstat-no" title="statement not covered" >0,</span>
+      dec = <span class="cstat-no" title="statement not covered" >'',</span>
+      tmp_arr = <span class="cstat-no" title="statement not covered" >[]</span>
+&nbsp;
+<span class="cstat-no" title="statement not covered" >  if (!data) {</span>
+<span class="cstat-no" title="statement not covered" >      return data</span>
+  }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >  data += ''</span>
+&nbsp;
+<span class="cstat-no" title="statement not covered" >  do { // unpack four hexets into three octets using index points in b64</span>
+<span class="cstat-no" title="statement not covered" >      h1 = b64.indexOf(data.charAt(i++))</span>
+<span class="cstat-no" title="statement not covered" >      h2 = b64.indexOf(data.charAt(i++))</span>
+<span class="cstat-no" title="statement not covered" >      h3 = b64.indexOf(data.charAt(i++))</span>
+<span class="cstat-no" title="statement not covered" >      h4 = b64.indexOf(data.charAt(i++))</span>
+&nbsp;
+<span class="cstat-no" title="statement not covered" >      bits = h1 &lt;&lt; 18 | h2 &lt;&lt; 12 | h3 &lt;&lt; 6 | h4</span>
+&nbsp;
+<span class="cstat-no" title="statement not covered" >      o1 = bits &gt;&gt; 16 &amp; 0xff</span>
+<span class="cstat-no" title="statement not covered" >      o2 = bits &gt;&gt; 8 &amp; 0xff</span>
+<span class="cstat-no" title="statement not covered" >      o3 = bits &amp; 0xff</span>
+&nbsp;
+<span class="cstat-no" title="statement not covered" >      if (h3 === 64) {</span>
+<span class="cstat-no" title="statement not covered" >          tmp_arr[ac++] = String.fromCharCode(o1)</span>
+      } else <span class="cstat-no" title="statement not covered" >if (h4 === 64) {</span>
+<span class="cstat-no" title="statement not covered" >          tmp_arr[ac++] = String.fromCharCode(o1, o2)</span>
+      } else {
+<span class="cstat-no" title="statement not covered" >          tmp_arr[ac++] = String.fromCharCode(o1, o2, o3)</span>
+      }
+  } while (i &lt; data.length)
+&nbsp;
+<span class="cstat-no" title="statement not covered" >  dec = tmp_arr.join('')</span>
+&nbsp;
+<span class="cstat-no" title="statement not covered" >  return dec</span>
+}
+&nbsp;
+export function urlSafeBase64Encode(v: any) {
+  v = base64Encode(v)
+  return v.replace(/\//g, '_').replace(/\+/g, '-')
+}
+&nbsp;
+export function <span class="fstat-no" title="function not covered" >urlSafeBase64Decode(</span>v: any){
+<span class="cstat-no" title="statement not covered" >  v = v.replace(/_/g, '/').replace(/-/g, '+')</span>
+<span class="cstat-no" title="statement not covered" >  return base64Decode(v)</span>
+}
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Thu Apr 22 2021 01:38:54 GMT+0000 (Coordinated Universal Time)
+            </div>
+        </div>
+        <script src="../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../sorter.js"></script>
+        <script src="../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 176 - 0
qiniu-js/coverage/lcov-report/src/config.ts.html

@@ -0,0 +1,176 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for src/config.ts</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../prettify.css" />
+    <link rel="stylesheet" href="../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../index.html">All files</a> / <a href="index.html">src</a> config.ts</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>2/2</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>0/0</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>0/0</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>2/2</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line high'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a>
+<a name='L17'></a><a href='#L17'>17</a>
+<a name='L18'></a><a href='#L18'>18</a>
+<a name='L19'></a><a href='#L19'>19</a>
+<a name='L20'></a><a href='#L20'>20</a>
+<a name='L21'></a><a href='#L21'>21</a>
+<a name='L22'></a><a href='#L22'>22</a>
+<a name='L23'></a><a href='#L23'>23</a>
+<a name='L24'></a><a href='#L24'>24</a>
+<a name='L25'></a><a href='#L25'>25</a>
+<a name='L26'></a><a href='#L26'>26</a>
+<a name='L27'></a><a href='#L27'>27</a>
+<a name='L28'></a><a href='#L28'>28</a>
+<a name='L29'></a><a href='#L29'>29</a>
+<a name='L30'></a><a href='#L30'>30</a>
+<a name='L31'></a><a href='#L31'>31</a>
+<a name='L32'></a><a href='#L32'>32</a>
+<a name='L33'></a><a href='#L33'>33</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">/** 上传区域 */
+export const region = {
+  z0: 'z0',
+  z1: 'z1',
+  z2: 'z2',
+  na0: 'na0',
+  as0: 'as0'
+} as const
+&nbsp;
+/** 上传区域对应的 host */
+export const regionUphostMap = {
+  [region.z0]: {
+    srcUphost: 'up.qiniup.com',
+    cdnUphost: 'upload.qiniup.com'
+  },
+  [region.z1]: {
+    srcUphost: 'up-z1.qiniup.com',
+    cdnUphost: 'upload-z1.qiniup.com'
+  },
+  [region.z2]: {
+    srcUphost: 'up-z2.qiniup.com',
+    cdnUphost: 'upload-z2.qiniup.com'
+  },
+  [region.na0]: {
+    srcUphost: 'up-na0.qiniup.com',
+    cdnUphost: 'upload-na0.qiniup.com'
+  },
+  [region.as0]: {
+    srcUphost: 'up-as0.qiniup.com',
+    cdnUphost: 'upload-as0.qiniup.com'
+  }
+} as const
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Thu Apr 22 2021 01:38:54 GMT+0000 (Coordinated Universal Time)
+            </div>
+        </div>
+        <script src="../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../sorter.js"></script>
+        <script src="../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 668 - 0
qiniu-js/coverage/lcov-report/src/image.ts.html

@@ -0,0 +1,668 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for src/image.ts</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../prettify.css" />
+    <link rel="stylesheet" href="../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../index.html">All files</a> / <a href="index.html">src</a> image.ts</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">56.57% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>56/99</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">42.55% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>40/94</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">57.14% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>4/7</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">57.73% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>56/97</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line medium'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a>
+<a name='L17'></a><a href='#L17'>17</a>
+<a name='L18'></a><a href='#L18'>18</a>
+<a name='L19'></a><a href='#L19'>19</a>
+<a name='L20'></a><a href='#L20'>20</a>
+<a name='L21'></a><a href='#L21'>21</a>
+<a name='L22'></a><a href='#L22'>22</a>
+<a name='L23'></a><a href='#L23'>23</a>
+<a name='L24'></a><a href='#L24'>24</a>
+<a name='L25'></a><a href='#L25'>25</a>
+<a name='L26'></a><a href='#L26'>26</a>
+<a name='L27'></a><a href='#L27'>27</a>
+<a name='L28'></a><a href='#L28'>28</a>
+<a name='L29'></a><a href='#L29'>29</a>
+<a name='L30'></a><a href='#L30'>30</a>
+<a name='L31'></a><a href='#L31'>31</a>
+<a name='L32'></a><a href='#L32'>32</a>
+<a name='L33'></a><a href='#L33'>33</a>
+<a name='L34'></a><a href='#L34'>34</a>
+<a name='L35'></a><a href='#L35'>35</a>
+<a name='L36'></a><a href='#L36'>36</a>
+<a name='L37'></a><a href='#L37'>37</a>
+<a name='L38'></a><a href='#L38'>38</a>
+<a name='L39'></a><a href='#L39'>39</a>
+<a name='L40'></a><a href='#L40'>40</a>
+<a name='L41'></a><a href='#L41'>41</a>
+<a name='L42'></a><a href='#L42'>42</a>
+<a name='L43'></a><a href='#L43'>43</a>
+<a name='L44'></a><a href='#L44'>44</a>
+<a name='L45'></a><a href='#L45'>45</a>
+<a name='L46'></a><a href='#L46'>46</a>
+<a name='L47'></a><a href='#L47'>47</a>
+<a name='L48'></a><a href='#L48'>48</a>
+<a name='L49'></a><a href='#L49'>49</a>
+<a name='L50'></a><a href='#L50'>50</a>
+<a name='L51'></a><a href='#L51'>51</a>
+<a name='L52'></a><a href='#L52'>52</a>
+<a name='L53'></a><a href='#L53'>53</a>
+<a name='L54'></a><a href='#L54'>54</a>
+<a name='L55'></a><a href='#L55'>55</a>
+<a name='L56'></a><a href='#L56'>56</a>
+<a name='L57'></a><a href='#L57'>57</a>
+<a name='L58'></a><a href='#L58'>58</a>
+<a name='L59'></a><a href='#L59'>59</a>
+<a name='L60'></a><a href='#L60'>60</a>
+<a name='L61'></a><a href='#L61'>61</a>
+<a name='L62'></a><a href='#L62'>62</a>
+<a name='L63'></a><a href='#L63'>63</a>
+<a name='L64'></a><a href='#L64'>64</a>
+<a name='L65'></a><a href='#L65'>65</a>
+<a name='L66'></a><a href='#L66'>66</a>
+<a name='L67'></a><a href='#L67'>67</a>
+<a name='L68'></a><a href='#L68'>68</a>
+<a name='L69'></a><a href='#L69'>69</a>
+<a name='L70'></a><a href='#L70'>70</a>
+<a name='L71'></a><a href='#L71'>71</a>
+<a name='L72'></a><a href='#L72'>72</a>
+<a name='L73'></a><a href='#L73'>73</a>
+<a name='L74'></a><a href='#L74'>74</a>
+<a name='L75'></a><a href='#L75'>75</a>
+<a name='L76'></a><a href='#L76'>76</a>
+<a name='L77'></a><a href='#L77'>77</a>
+<a name='L78'></a><a href='#L78'>78</a>
+<a name='L79'></a><a href='#L79'>79</a>
+<a name='L80'></a><a href='#L80'>80</a>
+<a name='L81'></a><a href='#L81'>81</a>
+<a name='L82'></a><a href='#L82'>82</a>
+<a name='L83'></a><a href='#L83'>83</a>
+<a name='L84'></a><a href='#L84'>84</a>
+<a name='L85'></a><a href='#L85'>85</a>
+<a name='L86'></a><a href='#L86'>86</a>
+<a name='L87'></a><a href='#L87'>87</a>
+<a name='L88'></a><a href='#L88'>88</a>
+<a name='L89'></a><a href='#L89'>89</a>
+<a name='L90'></a><a href='#L90'>90</a>
+<a name='L91'></a><a href='#L91'>91</a>
+<a name='L92'></a><a href='#L92'>92</a>
+<a name='L93'></a><a href='#L93'>93</a>
+<a name='L94'></a><a href='#L94'>94</a>
+<a name='L95'></a><a href='#L95'>95</a>
+<a name='L96'></a><a href='#L96'>96</a>
+<a name='L97'></a><a href='#L97'>97</a>
+<a name='L98'></a><a href='#L98'>98</a>
+<a name='L99'></a><a href='#L99'>99</a>
+<a name='L100'></a><a href='#L100'>100</a>
+<a name='L101'></a><a href='#L101'>101</a>
+<a name='L102'></a><a href='#L102'>102</a>
+<a name='L103'></a><a href='#L103'>103</a>
+<a name='L104'></a><a href='#L104'>104</a>
+<a name='L105'></a><a href='#L105'>105</a>
+<a name='L106'></a><a href='#L106'>106</a>
+<a name='L107'></a><a href='#L107'>107</a>
+<a name='L108'></a><a href='#L108'>108</a>
+<a name='L109'></a><a href='#L109'>109</a>
+<a name='L110'></a><a href='#L110'>110</a>
+<a name='L111'></a><a href='#L111'>111</a>
+<a name='L112'></a><a href='#L112'>112</a>
+<a name='L113'></a><a href='#L113'>113</a>
+<a name='L114'></a><a href='#L114'>114</a>
+<a name='L115'></a><a href='#L115'>115</a>
+<a name='L116'></a><a href='#L116'>116</a>
+<a name='L117'></a><a href='#L117'>117</a>
+<a name='L118'></a><a href='#L118'>118</a>
+<a name='L119'></a><a href='#L119'>119</a>
+<a name='L120'></a><a href='#L120'>120</a>
+<a name='L121'></a><a href='#L121'>121</a>
+<a name='L122'></a><a href='#L122'>122</a>
+<a name='L123'></a><a href='#L123'>123</a>
+<a name='L124'></a><a href='#L124'>124</a>
+<a name='L125'></a><a href='#L125'>125</a>
+<a name='L126'></a><a href='#L126'>126</a>
+<a name='L127'></a><a href='#L127'>127</a>
+<a name='L128'></a><a href='#L128'>128</a>
+<a name='L129'></a><a href='#L129'>129</a>
+<a name='L130'></a><a href='#L130'>130</a>
+<a name='L131'></a><a href='#L131'>131</a>
+<a name='L132'></a><a href='#L132'>132</a>
+<a name='L133'></a><a href='#L133'>133</a>
+<a name='L134'></a><a href='#L134'>134</a>
+<a name='L135'></a><a href='#L135'>135</a>
+<a name='L136'></a><a href='#L136'>136</a>
+<a name='L137'></a><a href='#L137'>137</a>
+<a name='L138'></a><a href='#L138'>138</a>
+<a name='L139'></a><a href='#L139'>139</a>
+<a name='L140'></a><a href='#L140'>140</a>
+<a name='L141'></a><a href='#L141'>141</a>
+<a name='L142'></a><a href='#L142'>142</a>
+<a name='L143'></a><a href='#L143'>143</a>
+<a name='L144'></a><a href='#L144'>144</a>
+<a name='L145'></a><a href='#L145'>145</a>
+<a name='L146'></a><a href='#L146'>146</a>
+<a name='L147'></a><a href='#L147'>147</a>
+<a name='L148'></a><a href='#L148'>148</a>
+<a name='L149'></a><a href='#L149'>149</a>
+<a name='L150'></a><a href='#L150'>150</a>
+<a name='L151'></a><a href='#L151'>151</a>
+<a name='L152'></a><a href='#L152'>152</a>
+<a name='L153'></a><a href='#L153'>153</a>
+<a name='L154'></a><a href='#L154'>154</a>
+<a name='L155'></a><a href='#L155'>155</a>
+<a name='L156'></a><a href='#L156'>156</a>
+<a name='L157'></a><a href='#L157'>157</a>
+<a name='L158'></a><a href='#L158'>158</a>
+<a name='L159'></a><a href='#L159'>159</a>
+<a name='L160'></a><a href='#L160'>160</a>
+<a name='L161'></a><a href='#L161'>161</a>
+<a name='L162'></a><a href='#L162'>162</a>
+<a name='L163'></a><a href='#L163'>163</a>
+<a name='L164'></a><a href='#L164'>164</a>
+<a name='L165'></a><a href='#L165'>165</a>
+<a name='L166'></a><a href='#L166'>166</a>
+<a name='L167'></a><a href='#L167'>167</a>
+<a name='L168'></a><a href='#L168'>168</a>
+<a name='L169'></a><a href='#L169'>169</a>
+<a name='L170'></a><a href='#L170'>170</a>
+<a name='L171'></a><a href='#L171'>171</a>
+<a name='L172'></a><a href='#L172'>172</a>
+<a name='L173'></a><a href='#L173'>173</a>
+<a name='L174'></a><a href='#L174'>174</a>
+<a name='L175'></a><a href='#L175'>175</a>
+<a name='L176'></a><a href='#L176'>176</a>
+<a name='L177'></a><a href='#L177'>177</a>
+<a name='L178'></a><a href='#L178'>178</a>
+<a name='L179'></a><a href='#L179'>179</a>
+<a name='L180'></a><a href='#L180'>180</a>
+<a name='L181'></a><a href='#L181'>181</a>
+<a name='L182'></a><a href='#L182'>182</a>
+<a name='L183'></a><a href='#L183'>183</a>
+<a name='L184'></a><a href='#L184'>184</a>
+<a name='L185'></a><a href='#L185'>185</a>
+<a name='L186'></a><a href='#L186'>186</a>
+<a name='L187'></a><a href='#L187'>187</a>
+<a name='L188'></a><a href='#L188'>188</a>
+<a name='L189'></a><a href='#L189'>189</a>
+<a name='L190'></a><a href='#L190'>190</a>
+<a name='L191'></a><a href='#L191'>191</a>
+<a name='L192'></a><a href='#L192'>192</a>
+<a name='L193'></a><a href='#L193'>193</a>
+<a name='L194'></a><a href='#L194'>194</a>
+<a name='L195'></a><a href='#L195'>195</a>
+<a name='L196'></a><a href='#L196'>196</a>
+<a name='L197'></a><a href='#L197'>197</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">5x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">8x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">4x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import { request } from './utils'
+import { urlSafeBase64Encode } from './base64'
+&nbsp;
+export interface ImageViewOptions {
+  mode: number
+  format?: string
+  w?: number
+  h?: number
+  q?: number
+}
+&nbsp;
+export interface ImageWatermark {
+  image: string
+  mode: number
+  fontsize?: number
+  dissolve?: number
+  dx?: number
+  dy?: number
+  gravity?: string
+  text?: string
+  font?: string
+  fill?: string
+}
+&nbsp;
+export interface ImageMogr2 {
+  'auto-orient'?: boolean
+  strip?: boolean
+  thumbnail?: number
+  crop?: number
+  gravity?: number
+  format?: number
+  blur?: number
+  quality?: number
+  rotate?: number
+}
+&nbsp;
+type Pipeline =
+  | (ImageWatermark &amp; { fop: 'watermark' })
+  | (ImageViewOptions &amp; { fop: 'imageView2' })
+  | (ImageMogr2 &amp; { fop: 'imageMogr2' })
+&nbsp;
+export interface Entry {
+  domain: string
+  key: string
+}
+&nbsp;
+function getImageUrl(key: string, domain: string) {
+  key = encodeURIComponent(key)
+  <span class="missing-if-branch" title="else path not taken" >E</span>if (domain.slice(domain.length - 1) !== '/') {
+    domain += '/'
+  }
+&nbsp;
+  return domain + key
+}
+&nbsp;
+export function imageView2(op: ImageViewOptions, key?: string, domain?: string) {
+  <span class="missing-if-branch" title="if path not taken" >I</span>if (!/^\d$/.test(String(op.mode))) {
+<span class="cstat-no" title="statement not covered" >    throw 'mode should be number in imageView2'</span>
+  }
+&nbsp;
+  const { mode, w, h, q, format } = op
+&nbsp;
+  <span class="missing-if-branch" title="if path not taken" >I</span>if (!w &amp;&amp; !h) {
+<span class="cstat-no" title="statement not covered" >    throw 'param w and h is empty in imageView2'</span>
+  }
+&nbsp;
+  let imageUrl = 'imageView2/' + encodeURIComponent(mode)
+  imageUrl += w ? <span class="branch-0 cbranch-no" title="branch not covered" >'/w/' + encodeURIComponent(w) </span>: ''
+  imageUrl += h ? '/h/' + encodeURIComponent(h) : <span class="branch-1 cbranch-no" title="branch not covered" >''</span>
+  imageUrl += q ? '/q/' + encodeURIComponent(q) : <span class="branch-1 cbranch-no" title="branch not covered" >''</span>
+  imageUrl += format ? <span class="branch-0 cbranch-no" title="branch not covered" >'/format/' + encodeURIComponent(format) </span>: ''
+  <span class="missing-if-branch" title="else path not taken" >E</span>if (key &amp;&amp; domain) {
+    imageUrl = getImageUrl(key, domain) + '?' + imageUrl
+  }
+  return imageUrl
+}
+&nbsp;
+// invoke the imageMogr2 api of Qiniu
+export function imageMogr2(op: ImageMogr2, key?: string, domain?: string) {
+  const autoOrient = op['auto-orient']
+  const { thumbnail, strip, gravity, crop, quality, rotate, format, blur } = op
+&nbsp;
+  let imageUrl = 'imageMogr2'
+&nbsp;
+  imageUrl += autoOrient ? <span class="branch-0 cbranch-no" title="branch not covered" >'/auto-orient' </span>: ''
+  imageUrl += thumbnail ? '/thumbnail/' + encodeURIComponent(thumbnail) : <span class="branch-1 cbranch-no" title="branch not covered" >''</span>
+  imageUrl += strip ? '/strip' : <span class="branch-1 cbranch-no" title="branch not covered" >''</span>
+  imageUrl += gravity ? '/gravity/' + encodeURIComponent(gravity) : <span class="branch-1 cbranch-no" title="branch not covered" >''</span>
+  imageUrl += quality ? '/quality/' + encodeURIComponent(quality) : <span class="branch-1 cbranch-no" title="branch not covered" >''</span>
+  imageUrl += crop ? '/crop/' + encodeURIComponent(crop) : <span class="branch-1 cbranch-no" title="branch not covered" >''</span>
+  imageUrl += rotate ? '/rotate/' + encodeURIComponent(rotate) : <span class="branch-1 cbranch-no" title="branch not covered" >''</span>
+  imageUrl += format ? '/format/' + encodeURIComponent(format) : <span class="branch-1 cbranch-no" title="branch not covered" >''</span>
+  imageUrl += blur ? '/blur/' + encodeURIComponent(blur) : <span class="branch-1 cbranch-no" title="branch not covered" >''</span>
+  <span class="missing-if-branch" title="else path not taken" >E</span>if (key &amp;&amp; domain) {
+    imageUrl = getImageUrl(key, domain) + '?' + imageUrl
+  }
+  return imageUrl
+}
+&nbsp;
+// invoke the watermark api of Qiniu
+export function watermark(op: ImageWatermark, key?: string, domain?: string) {
+  const mode = op.mode
+  <span class="missing-if-branch" title="if path not taken" >I</span>if (!mode) {
+<span class="cstat-no" title="statement not covered" >    throw "mode can't be empty in watermark"</span>
+  }
+&nbsp;
+  let imageUrl = 'watermark/' + mode
+  if (mode !== 1 &amp;&amp; mode !== 2) {
+    throw 'mode is wrong'
+  }
+&nbsp;
+  <span class="missing-if-branch" title="else path not taken" >E</span>if (mode === 1) {
+    const image = op.image
+    <span class="missing-if-branch" title="if path not taken" >I</span>if (!image) {
+<span class="cstat-no" title="statement not covered" >      throw "image can't be empty in watermark"</span>
+    }
+    imageUrl += image ? '/image/' + urlSafeBase64Encode(image) : <span class="branch-1 cbranch-no" title="branch not covered" >''</span>
+  }
+&nbsp;
+  <span class="missing-if-branch" title="if path not taken" >I</span>if (mode === 2) {
+    const { text, font, fontsize, fill } = <span class="cstat-no" title="statement not covered" >op</span>
+<span class="cstat-no" title="statement not covered" >    if (!text) {</span>
+<span class="cstat-no" title="statement not covered" >      throw "text can't be empty in watermark"</span>
+    }
+<span class="cstat-no" title="statement not covered" >    imageUrl += text ? '/text/' + urlSafeBase64Encode(text) : ''</span>
+<span class="cstat-no" title="statement not covered" >    imageUrl += font ? '/font/' + urlSafeBase64Encode(font) : ''</span>
+<span class="cstat-no" title="statement not covered" >    imageUrl += fontsize ? '/fontsize/' + fontsize : ''</span>
+<span class="cstat-no" title="statement not covered" >    imageUrl += fill ? '/fill/' + urlSafeBase64Encode(fill) : ''</span>
+  }
+&nbsp;
+  const { dissolve, gravity, dx, dy } = op
+&nbsp;
+  imageUrl += dissolve ? '/dissolve/' + encodeURIComponent(dissolve) : <span class="branch-1 cbranch-no" title="branch not covered" >''</span>
+  imageUrl += gravity ? <span class="branch-0 cbranch-no" title="branch not covered" >'/gravity/' + encodeURIComponent(gravity) </span>: ''
+  imageUrl += dx ? '/dx/' + encodeURIComponent(dx) : <span class="branch-1 cbranch-no" title="branch not covered" >''</span>
+  imageUrl += dy ? '/dy/' + encodeURIComponent(dy) : <span class="branch-1 cbranch-no" title="branch not covered" >''</span>
+  <span class="missing-if-branch" title="else path not taken" >E</span>if (key &amp;&amp; domain) {
+    imageUrl = getImageUrl(key, domain) + '?' + imageUrl
+  }
+  return imageUrl
+}
+&nbsp;
+// invoke the imageInfo api of Qiniu
+export function <span class="fstat-no" title="function not covered" >imageInfo(</span>key: string, domain: string) {
+  const url = <span class="cstat-no" title="statement not covered" >getImageUrl(key, domain) + '?imageInfo'</span>
+<span class="cstat-no" title="statement not covered" >  return request(url, { method: 'GET' })</span>
+}
+&nbsp;
+// invoke the exif api of Qiniu
+export function <span class="fstat-no" title="function not covered" >exif(</span>key: string, domain: string) {
+  const url = <span class="cstat-no" title="statement not covered" >getImageUrl(key, domain) + '?exif'</span>
+<span class="cstat-no" title="statement not covered" >  return request(url, { method: 'GET' })</span>
+}
+&nbsp;
+export function <span class="fstat-no" title="function not covered" >pipeline(</span>arr: Pipeline[], key?: string, domain?: string) {
+  const isArray = <span class="cstat-no" title="statement not covered" >Object.prototype.toString.call(arr) === '[object Array]'</span>
+  let option: Pipeline
+  let errOp = <span class="cstat-no" title="statement not covered" >false</span>
+  let imageUrl = <span class="cstat-no" title="statement not covered" >''</span>
+<span class="cstat-no" title="statement not covered" >  if (isArray) {</span>
+<span class="cstat-no" title="statement not covered" >    for (let i = <span class="cstat-no" title="statement not covered" >0,</span> len = <span class="cstat-no" title="statement not covered" >arr.length;</span> i &lt; len; i++) {</span>
+<span class="cstat-no" title="statement not covered" >      option = arr[i]</span>
+<span class="cstat-no" title="statement not covered" >      if (!option.fop) {</span>
+<span class="cstat-no" title="statement not covered" >        throw "fop can't be empty in pipeline"</span>
+      }
+<span class="cstat-no" title="statement not covered" >      switch (option.fop) {</span>
+        case 'watermark':
+<span class="cstat-no" title="statement not covered" >          imageUrl += watermark(option) + '|'</span>
+<span class="cstat-no" title="statement not covered" >          break</span>
+        case 'imageView2':
+<span class="cstat-no" title="statement not covered" >          imageUrl += imageView2(option) + '|'</span>
+<span class="cstat-no" title="statement not covered" >          break</span>
+        case 'imageMogr2':
+<span class="cstat-no" title="statement not covered" >          imageUrl += imageMogr2(option) + '|'</span>
+<span class="cstat-no" title="statement not covered" >          break</span>
+        default:
+<span class="cstat-no" title="statement not covered" >          errOp = true</span>
+<span class="cstat-no" title="statement not covered" >          break</span>
+      }
+<span class="cstat-no" title="statement not covered" >      if (errOp) {</span>
+<span class="cstat-no" title="statement not covered" >        throw 'fop is wrong in pipeline'</span>
+      }
+    }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    if (key &amp;&amp; domain) {</span>
+<span class="cstat-no" title="statement not covered" >      imageUrl = getImageUrl(key, domain) + '?' + imageUrl</span>
+      const length = <span class="cstat-no" title="statement not covered" >imageUrl.length</span>
+<span class="cstat-no" title="statement not covered" >      if (imageUrl.slice(length - 1) === '|') {</span>
+<span class="cstat-no" title="statement not covered" >        imageUrl = imageUrl.slice(0, length - 1)</span>
+      }
+    }
+<span class="cstat-no" title="statement not covered" >    return imageUrl</span>
+  }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >  throw "pipeline's first param should be array"</span>
+}
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Thu Apr 22 2021 01:38:54 GMT+0000 (Coordinated Universal Time)
+            </div>
+        </div>
+        <script src="../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../sorter.js"></script>
+        <script src="../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 186 - 0
qiniu-js/coverage/lcov-report/src/index.html

@@ -0,0 +1,186 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for src</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../prettify.css" />
+    <link rel="stylesheet" href="../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../index.html">All files</a> src</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">44.64% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>179/401</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">28.43% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>56/197</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">33.33% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>20/60</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">44.39% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>170/383</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line low'></div>
+    <div class="pad1">
+<table class="coverage-summary">
+<thead>
+<tr>
+   <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
+   <th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
+   <th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
+   <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
+   <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
+   <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
+   <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
+</tr>
+</thead>
+<tbody><tr>
+	<td class="file medium" data-value="api.ts"><a href="api.ts.html">api.ts</a></td>
+	<td data-value="63.89" class="pic medium">
+	<div class="chart"><div class="cover-fill" style="width: 63%"></div><div class="cover-empty" style="width: 37%"></div></div>
+	</td>
+	<td data-value="63.89" class="pct medium">63.89%</td>
+	<td data-value="36" class="abs medium">23/36</td>
+	<td data-value="50" class="pct medium">50%</td>
+	<td data-value="12" class="abs medium">6/12</td>
+	<td data-value="28.57" class="pct low">28.57%</td>
+	<td data-value="7" class="abs low">2/7</td>
+	<td data-value="62.86" class="pct medium">62.86%</td>
+	<td data-value="35" class="abs medium">22/35</td>
+	</tr>
+
+<tr>
+	<td class="file medium" data-value="base64.ts"><a href="base64.ts.html">base64.ts</a></td>
+	<td data-value="50" class="pic medium">
+	<div class="chart"><div class="cover-fill" style="width: 50%"></div><div class="cover-empty" style="width: 50%"></div></div>
+	</td>
+	<td data-value="50" class="pct medium">50%</td>
+	<td data-value="90" class="abs medium">45/90</td>
+	<td data-value="25" class="pct low">25%</td>
+	<td data-value="32" class="abs low">8/32</td>
+	<td data-value="60" class="pct medium">60%</td>
+	<td data-value="5" class="abs medium">3/5</td>
+	<td data-value="49.44" class="pct low">49.44%</td>
+	<td data-value="89" class="abs low">44/89</td>
+	</tr>
+
+<tr>
+	<td class="file high" data-value="config.ts"><a href="config.ts.html">config.ts</a></td>
+	<td data-value="100" class="pic high">
+	<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
+	</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="2" class="abs high">2/2</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="0" class="abs high">0/0</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="0" class="abs high">0/0</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="2" class="abs high">2/2</td>
+	</tr>
+
+<tr>
+	<td class="file medium" data-value="image.ts"><a href="image.ts.html">image.ts</a></td>
+	<td data-value="56.57" class="pic medium">
+	<div class="chart"><div class="cover-fill" style="width: 56%"></div><div class="cover-empty" style="width: 44%"></div></div>
+	</td>
+	<td data-value="56.57" class="pct medium">56.57%</td>
+	<td data-value="99" class="abs medium">56/99</td>
+	<td data-value="42.55" class="pct low">42.55%</td>
+	<td data-value="94" class="abs low">40/94</td>
+	<td data-value="57.14" class="pct medium">57.14%</td>
+	<td data-value="7" class="abs medium">4/7</td>
+	<td data-value="57.73" class="pct medium">57.73%</td>
+	<td data-value="97" class="abs medium">56/97</td>
+	</tr>
+
+<tr>
+	<td class="file high" data-value="pool.ts"><a href="pool.ts.html">pool.ts</a></td>
+	<td data-value="96.55" class="pic high">
+	<div class="chart"><div class="cover-fill" style="width: 96%"></div><div class="cover-empty" style="width: 4%"></div></div>
+	</td>
+	<td data-value="96.55" class="pct high">96.55%</td>
+	<td data-value="29" class="abs high">28/29</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="0" class="abs high">0/0</td>
+	<td data-value="90.91" class="pct high">90.91%</td>
+	<td data-value="11" class="abs high">10/11</td>
+	<td data-value="95.45" class="pct high">95.45%</td>
+	<td data-value="22" class="abs high">21/22</td>
+	</tr>
+
+<tr>
+	<td class="file low" data-value="utils.ts"><a href="utils.ts.html">utils.ts</a></td>
+	<td data-value="17.24" class="pic low">
+	<div class="chart"><div class="cover-fill" style="width: 17%"></div><div class="cover-empty" style="width: 83%"></div></div>
+	</td>
+	<td data-value="17.24" class="pct low">17.24%</td>
+	<td data-value="145" class="abs low">25/145</td>
+	<td data-value="3.39" class="pct low">3.39%</td>
+	<td data-value="59" class="abs low">2/59</td>
+	<td data-value="3.33" class="pct low">3.33%</td>
+	<td data-value="30" class="abs low">1/30</td>
+	<td data-value="18.12" class="pct low">18.12%</td>
+	<td data-value="138" class="abs low">25/138</td>
+	</tr>
+
+</tbody>
+</table>
+</div>
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Thu Apr 22 2021 01:38:54 GMT+0000 (Coordinated Universal Time)
+            </div>
+        </div>
+        <script src="../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../sorter.js"></script>
+        <script src="../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 126 - 0
qiniu-js/coverage/lcov-report/src/logger/index.html

@@ -0,0 +1,126 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for src/logger</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../prettify.css" />
+    <link rel="stylesheet" href="../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../index.html">All files</a> src/logger</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">97.73% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>43/44</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">69.23% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>27/39</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>8/8</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">97.14% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>34/35</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line high'></div>
+    <div class="pad1">
+<table class="coverage-summary">
+<thead>
+<tr>
+   <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
+   <th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
+   <th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
+   <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
+   <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
+   <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
+   <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
+</tr>
+</thead>
+<tbody><tr>
+	<td class="file high" data-value="index.ts"><a href="index.ts.html">index.ts</a></td>
+	<td data-value="96.88" class="pic high">
+	<div class="chart"><div class="cover-fill" style="width: 96%"></div><div class="cover-empty" style="width: 4%"></div></div>
+	</td>
+	<td data-value="96.88" class="pct high">96.88%</td>
+	<td data-value="32" class="abs high">31/32</td>
+	<td data-value="83.33" class="pct high">83.33%</td>
+	<td data-value="12" class="abs high">10/12</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="6" class="abs high">6/6</td>
+	<td data-value="95.83" class="pct high">95.83%</td>
+	<td data-value="24" class="abs high">23/24</td>
+	</tr>
+
+<tr>
+	<td class="file high" data-value="report-v3.ts"><a href="report-v3.ts.html">report-v3.ts</a></td>
+	<td data-value="100" class="pic high">
+	<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
+	</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="12" class="abs high">12/12</td>
+	<td data-value="62.96" class="pct medium">62.96%</td>
+	<td data-value="27" class="abs medium">17/27</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="2" class="abs high">2/2</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="11" class="abs high">11/11</td>
+	</tr>
+
+</tbody>
+</table>
+</div>
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Thu Apr 22 2021 01:38:54 GMT+0000 (Coordinated Universal Time)
+            </div>
+        </div>
+        <script src="../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../sorter.js"></script>
+        <script src="../../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 263 - 0
qiniu-js/coverage/lcov-report/src/logger/index.ts.html

@@ -0,0 +1,263 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for src/logger/index.ts</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../prettify.css" />
+    <link rel="stylesheet" href="../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../index.html">All files</a> / <a href="index.html">src/logger</a> index.ts</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">96.88% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>31/32</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">83.33% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>10/12</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>6/6</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">95.83% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>23/24</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line high'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a>
+<a name='L17'></a><a href='#L17'>17</a>
+<a name='L18'></a><a href='#L18'>18</a>
+<a name='L19'></a><a href='#L19'>19</a>
+<a name='L20'></a><a href='#L20'>20</a>
+<a name='L21'></a><a href='#L21'>21</a>
+<a name='L22'></a><a href='#L22'>22</a>
+<a name='L23'></a><a href='#L23'>23</a>
+<a name='L24'></a><a href='#L24'>24</a>
+<a name='L25'></a><a href='#L25'>25</a>
+<a name='L26'></a><a href='#L26'>26</a>
+<a name='L27'></a><a href='#L27'>27</a>
+<a name='L28'></a><a href='#L28'>28</a>
+<a name='L29'></a><a href='#L29'>29</a>
+<a name='L30'></a><a href='#L30'>30</a>
+<a name='L31'></a><a href='#L31'>31</a>
+<a name='L32'></a><a href='#L32'>32</a>
+<a name='L33'></a><a href='#L33'>33</a>
+<a name='L34'></a><a href='#L34'>34</a>
+<a name='L35'></a><a href='#L35'>35</a>
+<a name='L36'></a><a href='#L36'>36</a>
+<a name='L37'></a><a href='#L37'>37</a>
+<a name='L38'></a><a href='#L38'>38</a>
+<a name='L39'></a><a href='#L39'>39</a>
+<a name='L40'></a><a href='#L40'>40</a>
+<a name='L41'></a><a href='#L41'>41</a>
+<a name='L42'></a><a href='#L42'>42</a>
+<a name='L43'></a><a href='#L43'>43</a>
+<a name='L44'></a><a href='#L44'>44</a>
+<a name='L45'></a><a href='#L45'>45</a>
+<a name='L46'></a><a href='#L46'>46</a>
+<a name='L47'></a><a href='#L47'>47</a>
+<a name='L48'></a><a href='#L48'>48</a>
+<a name='L49'></a><a href='#L49'>49</a>
+<a name='L50'></a><a href='#L50'>50</a>
+<a name='L51'></a><a href='#L51'>51</a>
+<a name='L52'></a><a href='#L52'>52</a>
+<a name='L53'></a><a href='#L53'>53</a>
+<a name='L54'></a><a href='#L54'>54</a>
+<a name='L55'></a><a href='#L55'>55</a>
+<a name='L56'></a><a href='#L56'>56</a>
+<a name='L57'></a><a href='#L57'>57</a>
+<a name='L58'></a><a href='#L58'>58</a>
+<a name='L59'></a><a href='#L59'>59</a>
+<a name='L60'></a><a href='#L60'>60</a>
+<a name='L61'></a><a href='#L61'>61</a>
+<a name='L62'></a><a href='#L62'>62</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">9x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">9x</span>
+<span class="cline-any cline-yes">9x</span>
+<span class="cline-any cline-yes">9x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">12x</span>
+<span class="cline-any cline-yes">4x</span>
+<span class="cline-any cline-yes">4x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">12x</span>
+<span class="cline-any cline-yes">4x</span>
+<span class="cline-any cline-yes">4x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">12x</span>
+<span class="cline-any cline-yes">4x</span>
+<span class="cline-any cline-yes">4x</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import { reportV3, V3LogInfo } from './report-v3'
+&nbsp;
+export type LogLevel = 'INFO' | 'WARN' | 'ERROR' | 'OFF'
+&nbsp;
+export default class Logger {
+  private static id: number = 0
+&nbsp;
+  // 为每个类分配一个 id
+  // 用以区分不同的上传任务
+  private id = ++Logger.id
+&nbsp;
+  constructor(
+    private token: string,
+    private <span class="missing-if-branch" title="if path not taken" >I</span>disableReport = true,
+    private <span class="missing-if-branch" title="if path not taken" >I</span>level: LogLevel = 'OFF'
+  ) { }
+&nbsp;
+  /**
+   * @param  {V3LogInfo} data 上报的数据。
+   * @param  {boolean} retry 重试次数,可选,默认为 3。
+   * @description 向服务端上报统计信息。
+   */
+  report(data: V3LogInfo, retry?: number) {
+    if (this.disableReport) return
+    try { reportV3(this.token, data, retry) }
+    catch (error) { <span class="cstat-no" title="statement not covered" >console.warn(error) }</span>
+  }
+&nbsp;
+  /**
+   * @param  {unknown[]} ...args
+   * @description 输出 info 级别的调试信息。
+   */
+  info(...args: unknown[]) {
+    const allowLevel: LogLevel[] = ['INFO']
+    if (allowLevel.includes(this.level)) {
+      console.log(`Qiniu-JS-SDK [INFO][${this.id}]: `, ...args)
+    }
+  }
+&nbsp;
+  /**
+   * @param  {unknown[]} ...args
+   * @description 输出 warn 级别的调试信息。
+   */
+  warn(...args: unknown[]) {
+    const allowLevel: LogLevel[] = ['INFO', 'WARN']
+    if (allowLevel.includes(this.level)) {
+      console.warn(`Qiniu-JS-SDK [WARN][${this.id}]: `, ...args)
+    }
+  }
+&nbsp;
+  /**
+   * @param  {unknown[]} ...args
+   * @description 输出 error 级别的调试信息。
+   */
+  error(...args: unknown[]) {
+    const allowLevel: LogLevel[] = ['INFO', 'WARN', 'ERROR']
+    if (allowLevel.includes(this.level)) {
+      console.error(`Qiniu-JS-SDK [ERROR][${this.id}]: `, ...args)
+    }
+  }
+}
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Thu Apr 22 2021 01:38:54 GMT+0000 (Coordinated Universal Time)
+            </div>
+        </div>
+        <script src="../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../sorter.js"></script>
+        <script src="../../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 224 - 0
qiniu-js/coverage/lcov-report/src/logger/report-v3.ts.html

@@ -0,0 +1,224 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for src/logger/report-v3.ts</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../prettify.css" />
+    <link rel="stylesheet" href="../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../index.html">All files</a> / <a href="index.html">src/logger</a> report-v3.ts</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>12/12</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">62.96% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>17/27</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>2/2</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>11/11</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line high'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a>
+<a name='L17'></a><a href='#L17'>17</a>
+<a name='L18'></a><a href='#L18'>18</a>
+<a name='L19'></a><a href='#L19'>19</a>
+<a name='L20'></a><a href='#L20'>20</a>
+<a name='L21'></a><a href='#L21'>21</a>
+<a name='L22'></a><a href='#L22'>22</a>
+<a name='L23'></a><a href='#L23'>23</a>
+<a name='L24'></a><a href='#L24'>24</a>
+<a name='L25'></a><a href='#L25'>25</a>
+<a name='L26'></a><a href='#L26'>26</a>
+<a name='L27'></a><a href='#L27'>27</a>
+<a name='L28'></a><a href='#L28'>28</a>
+<a name='L29'></a><a href='#L29'>29</a>
+<a name='L30'></a><a href='#L30'>30</a>
+<a name='L31'></a><a href='#L31'>31</a>
+<a name='L32'></a><a href='#L32'>32</a>
+<a name='L33'></a><a href='#L33'>33</a>
+<a name='L34'></a><a href='#L34'>34</a>
+<a name='L35'></a><a href='#L35'>35</a>
+<a name='L36'></a><a href='#L36'>36</a>
+<a name='L37'></a><a href='#L37'>37</a>
+<a name='L38'></a><a href='#L38'>38</a>
+<a name='L39'></a><a href='#L39'>39</a>
+<a name='L40'></a><a href='#L40'>40</a>
+<a name='L41'></a><a href='#L41'>41</a>
+<a name='L42'></a><a href='#L42'>42</a>
+<a name='L43'></a><a href='#L43'>43</a>
+<a name='L44'></a><a href='#L44'>44</a>
+<a name='L45'></a><a href='#L45'>45</a>
+<a name='L46'></a><a href='#L46'>46</a>
+<a name='L47'></a><a href='#L47'>47</a>
+<a name='L48'></a><a href='#L48'>48</a>
+<a name='L49'></a><a href='#L49'>49</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">12x</span>
+<span class="cline-any cline-yes">11x</span>
+<span class="cline-any cline-yes">11x</span>
+<span class="cline-any cline-yes">11x</span>
+<span class="cline-any cline-yes">11x</span>
+<span class="cline-any cline-yes">11x</span>
+<span class="cline-any cline-yes">29x</span>
+<span class="cline-any cline-yes">7x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">11x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">11x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import { createXHR, getAuthHeaders } from '../utils'
+&nbsp;
+export interface V3LogInfo {
+  code: number
+  reqId: string
+  host: string
+  remoteIp: string
+  port: string
+  duration: number
+  time: number
+  bytesSent: number
+  upType: 'jssdk-h5'
+  size: number
+}
+&nbsp;
+/**
+ * @param  {string} token 上传使用的 token
+ * @param  {V3LogInfo} data 上报的统计数据
+ * @param  {number} retry 重试的次数,默认值 3
+ * @description v3 版本的日志上传接口,参考文档 https://github.com/qbox/product/blob/master/kodo/uplog.md#%E7%89%88%E6%9C%AC-3。
+ */
+export function reportV3(token: string, data: V3LogInfo, retry = 3) {
+  const xhr = createXHR()
+  xhr.open('POST', 'https://uplog.qbox.me/log/3')
+  xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
+  xhr.setRequestHeader('Authorization', getAuthHeaders(token).Authorization)
+  xhr.onreadystatechange = () =&gt; {
+    if (xhr.readyState === 4 &amp;&amp; xhr.status !== 200 &amp;&amp; retry &gt; 0) {
+      reportV3(token, data, retry - 1)
+    }
+  }
+&nbsp;
+  // 顺序参考:https://github.com/qbox/product/blob/master/kodo/uplog.md#%E7%89%88%E6%9C%AC-3
+  const stringifyData = [
+    data.code || <span class="branch-1 cbranch-no" title="branch not covered" >'',</span>
+    data.reqId || <span class="branch-1 cbranch-no" title="branch not covered" >'',</span>
+    data.host || <span class="branch-1 cbranch-no" title="branch not covered" >'',</span>
+    data.remoteIp || <span class="branch-1 cbranch-no" title="branch not covered" >'',</span>
+    data.port || <span class="branch-1 cbranch-no" title="branch not covered" >'',</span>
+    data.duration || <span class="branch-1 cbranch-no" title="branch not covered" >'',</span>
+    data.time || <span class="branch-1 cbranch-no" title="branch not covered" >'',</span>
+    data.bytesSent || <span class="branch-1 cbranch-no" title="branch not covered" >'',</span>
+    data.upType || <span class="branch-1 cbranch-no" title="branch not covered" >'',</span>
+    data.size || <span class="branch-1 cbranch-no" title="branch not covered" >''</span>
+  ].join(',')
+&nbsp;
+  xhr.send(stringifyData)
+}
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Thu Apr 22 2021 01:38:54 GMT+0000 (Coordinated Universal Time)
+            </div>
+        </div>
+        <script src="../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../sorter.js"></script>
+        <script src="../../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 218 - 0
qiniu-js/coverage/lcov-report/src/pool.ts.html

@@ -0,0 +1,218 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for src/pool.ts</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../prettify.css" />
+    <link rel="stylesheet" href="../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../index.html">All files</a> / <a href="index.html">src</a> pool.ts</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">96.55% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>28/29</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>0/0</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">90.91% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>10/11</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">95.45% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>21/22</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line high'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a>
+<a name='L17'></a><a href='#L17'>17</a>
+<a name='L18'></a><a href='#L18'>18</a>
+<a name='L19'></a><a href='#L19'>19</a>
+<a name='L20'></a><a href='#L20'>20</a>
+<a name='L21'></a><a href='#L21'>21</a>
+<a name='L22'></a><a href='#L22'>22</a>
+<a name='L23'></a><a href='#L23'>23</a>
+<a name='L24'></a><a href='#L24'>24</a>
+<a name='L25'></a><a href='#L25'>25</a>
+<a name='L26'></a><a href='#L26'>26</a>
+<a name='L27'></a><a href='#L27'>27</a>
+<a name='L28'></a><a href='#L28'>28</a>
+<a name='L29'></a><a href='#L29'>29</a>
+<a name='L30'></a><a href='#L30'>30</a>
+<a name='L31'></a><a href='#L31'>31</a>
+<a name='L32'></a><a href='#L32'>32</a>
+<a name='L33'></a><a href='#L33'>33</a>
+<a name='L34'></a><a href='#L34'>34</a>
+<a name='L35'></a><a href='#L35'>35</a>
+<a name='L36'></a><a href='#L36'>36</a>
+<a name='L37'></a><a href='#L37'>37</a>
+<a name='L38'></a><a href='#L38'>38</a>
+<a name='L39'></a><a href='#L39'>39</a>
+<a name='L40'></a><a href='#L40'>40</a>
+<a name='L41'></a><a href='#L41'>41</a>
+<a name='L42'></a><a href='#L42'>42</a>
+<a name='L43'></a><a href='#L43'>43</a>
+<a name='L44'></a><a href='#L44'>44</a>
+<a name='L45'></a><a href='#L45'>45</a>
+<a name='L46'></a><a href='#L46'>46</a>
+<a name='L47'></a><a href='#L47'>47</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">6x</span>
+<span class="cline-any cline-yes">6x</span>
+<span class="cline-any cline-yes">6x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">6x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">6x</span>
+<span class="cline-any cline-yes">12x</span>
+<span class="cline-any cline-yes">6x</span>
+<span class="cline-any cline-yes">6x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">11x</span>
+<span class="cline-any cline-yes">6x</span>
+<span class="cline-any cline-yes">6x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">12x</span>
+<span class="cline-any cline-yes">12x</span>
+<span class="cline-any cline-yes">12x</span>
+<span class="cline-any cline-yes">12x</span>
+<span class="cline-any cline-yes">6x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">export type RunTask&lt;T&gt; = (...args: T[]) =&gt; Promise&lt;void&gt;
+&nbsp;
+export interface QueueContent&lt;T&gt; {
+  task: T
+  resolve: () =&gt; void
+  reject: (err?: any) =&gt; void
+}
+&nbsp;
+export class Pool&lt;T&gt; {
+  queue: Array&lt;QueueContent&lt;T&gt;&gt; = []
+  processing: Array&lt;QueueContent&lt;T&gt;&gt; = []
+&nbsp;
+  constructor(private runTask: RunTask&lt;T&gt;, private limit: number) {}
+&nbsp;
+  enqueue(task: T) {
+    return new Promise&lt;void&gt;((resolve, reject) =&gt; {
+      this.queue.push({
+        task,
+        resolve,
+        reject
+      })
+      this.check()
+    })
+  }
+&nbsp;
+  run(item: QueueContent&lt;T&gt;) {
+    this.queue = this.queue.filter(v =&gt; v !== item)
+    this.processing.push(item)
+    this.runTask(item.task).then(
+      () =&gt; {
+        this.processing = this.processing.filter(v =&gt; v !== item)
+        item.resolve()
+        this.check()
+      },
+<span class="fstat-no" title="function not covered" >      err </span>=&gt; <span class="cstat-no" title="statement not covered" >item.reject(err)</span>
+    )
+  }
+&nbsp;
+  check() {
+    const processingNum = this.processing.length
+    const availableNum = this.limit - processingNum
+    this.queue.slice(0, availableNum).forEach(item =&gt; {
+      this.run(item)
+    })
+  }
+}
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Thu Apr 22 2021 01:38:54 GMT+0000 (Coordinated Universal Time)
+            </div>
+        </div>
+        <script src="../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../sorter.js"></script>
+        <script src="../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 854 - 0
qiniu-js/coverage/lcov-report/src/upload/base.ts.html

@@ -0,0 +1,854 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for src/upload/base.ts</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../prettify.css" />
+    <link rel="stylesheet" href="../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../index.html">All files</a> / <a href="index.html">src/upload</a> base.ts</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">15.48% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>13/84</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">0% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>0/28</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">10% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>1/10</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">16.05% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>13/81</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line low'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a>
+<a name='L17'></a><a href='#L17'>17</a>
+<a name='L18'></a><a href='#L18'>18</a>
+<a name='L19'></a><a href='#L19'>19</a>
+<a name='L20'></a><a href='#L20'>20</a>
+<a name='L21'></a><a href='#L21'>21</a>
+<a name='L22'></a><a href='#L22'>22</a>
+<a name='L23'></a><a href='#L23'>23</a>
+<a name='L24'></a><a href='#L24'>24</a>
+<a name='L25'></a><a href='#L25'>25</a>
+<a name='L26'></a><a href='#L26'>26</a>
+<a name='L27'></a><a href='#L27'>27</a>
+<a name='L28'></a><a href='#L28'>28</a>
+<a name='L29'></a><a href='#L29'>29</a>
+<a name='L30'></a><a href='#L30'>30</a>
+<a name='L31'></a><a href='#L31'>31</a>
+<a name='L32'></a><a href='#L32'>32</a>
+<a name='L33'></a><a href='#L33'>33</a>
+<a name='L34'></a><a href='#L34'>34</a>
+<a name='L35'></a><a href='#L35'>35</a>
+<a name='L36'></a><a href='#L36'>36</a>
+<a name='L37'></a><a href='#L37'>37</a>
+<a name='L38'></a><a href='#L38'>38</a>
+<a name='L39'></a><a href='#L39'>39</a>
+<a name='L40'></a><a href='#L40'>40</a>
+<a name='L41'></a><a href='#L41'>41</a>
+<a name='L42'></a><a href='#L42'>42</a>
+<a name='L43'></a><a href='#L43'>43</a>
+<a name='L44'></a><a href='#L44'>44</a>
+<a name='L45'></a><a href='#L45'>45</a>
+<a name='L46'></a><a href='#L46'>46</a>
+<a name='L47'></a><a href='#L47'>47</a>
+<a name='L48'></a><a href='#L48'>48</a>
+<a name='L49'></a><a href='#L49'>49</a>
+<a name='L50'></a><a href='#L50'>50</a>
+<a name='L51'></a><a href='#L51'>51</a>
+<a name='L52'></a><a href='#L52'>52</a>
+<a name='L53'></a><a href='#L53'>53</a>
+<a name='L54'></a><a href='#L54'>54</a>
+<a name='L55'></a><a href='#L55'>55</a>
+<a name='L56'></a><a href='#L56'>56</a>
+<a name='L57'></a><a href='#L57'>57</a>
+<a name='L58'></a><a href='#L58'>58</a>
+<a name='L59'></a><a href='#L59'>59</a>
+<a name='L60'></a><a href='#L60'>60</a>
+<a name='L61'></a><a href='#L61'>61</a>
+<a name='L62'></a><a href='#L62'>62</a>
+<a name='L63'></a><a href='#L63'>63</a>
+<a name='L64'></a><a href='#L64'>64</a>
+<a name='L65'></a><a href='#L65'>65</a>
+<a name='L66'></a><a href='#L66'>66</a>
+<a name='L67'></a><a href='#L67'>67</a>
+<a name='L68'></a><a href='#L68'>68</a>
+<a name='L69'></a><a href='#L69'>69</a>
+<a name='L70'></a><a href='#L70'>70</a>
+<a name='L71'></a><a href='#L71'>71</a>
+<a name='L72'></a><a href='#L72'>72</a>
+<a name='L73'></a><a href='#L73'>73</a>
+<a name='L74'></a><a href='#L74'>74</a>
+<a name='L75'></a><a href='#L75'>75</a>
+<a name='L76'></a><a href='#L76'>76</a>
+<a name='L77'></a><a href='#L77'>77</a>
+<a name='L78'></a><a href='#L78'>78</a>
+<a name='L79'></a><a href='#L79'>79</a>
+<a name='L80'></a><a href='#L80'>80</a>
+<a name='L81'></a><a href='#L81'>81</a>
+<a name='L82'></a><a href='#L82'>82</a>
+<a name='L83'></a><a href='#L83'>83</a>
+<a name='L84'></a><a href='#L84'>84</a>
+<a name='L85'></a><a href='#L85'>85</a>
+<a name='L86'></a><a href='#L86'>86</a>
+<a name='L87'></a><a href='#L87'>87</a>
+<a name='L88'></a><a href='#L88'>88</a>
+<a name='L89'></a><a href='#L89'>89</a>
+<a name='L90'></a><a href='#L90'>90</a>
+<a name='L91'></a><a href='#L91'>91</a>
+<a name='L92'></a><a href='#L92'>92</a>
+<a name='L93'></a><a href='#L93'>93</a>
+<a name='L94'></a><a href='#L94'>94</a>
+<a name='L95'></a><a href='#L95'>95</a>
+<a name='L96'></a><a href='#L96'>96</a>
+<a name='L97'></a><a href='#L97'>97</a>
+<a name='L98'></a><a href='#L98'>98</a>
+<a name='L99'></a><a href='#L99'>99</a>
+<a name='L100'></a><a href='#L100'>100</a>
+<a name='L101'></a><a href='#L101'>101</a>
+<a name='L102'></a><a href='#L102'>102</a>
+<a name='L103'></a><a href='#L103'>103</a>
+<a name='L104'></a><a href='#L104'>104</a>
+<a name='L105'></a><a href='#L105'>105</a>
+<a name='L106'></a><a href='#L106'>106</a>
+<a name='L107'></a><a href='#L107'>107</a>
+<a name='L108'></a><a href='#L108'>108</a>
+<a name='L109'></a><a href='#L109'>109</a>
+<a name='L110'></a><a href='#L110'>110</a>
+<a name='L111'></a><a href='#L111'>111</a>
+<a name='L112'></a><a href='#L112'>112</a>
+<a name='L113'></a><a href='#L113'>113</a>
+<a name='L114'></a><a href='#L114'>114</a>
+<a name='L115'></a><a href='#L115'>115</a>
+<a name='L116'></a><a href='#L116'>116</a>
+<a name='L117'></a><a href='#L117'>117</a>
+<a name='L118'></a><a href='#L118'>118</a>
+<a name='L119'></a><a href='#L119'>119</a>
+<a name='L120'></a><a href='#L120'>120</a>
+<a name='L121'></a><a href='#L121'>121</a>
+<a name='L122'></a><a href='#L122'>122</a>
+<a name='L123'></a><a href='#L123'>123</a>
+<a name='L124'></a><a href='#L124'>124</a>
+<a name='L125'></a><a href='#L125'>125</a>
+<a name='L126'></a><a href='#L126'>126</a>
+<a name='L127'></a><a href='#L127'>127</a>
+<a name='L128'></a><a href='#L128'>128</a>
+<a name='L129'></a><a href='#L129'>129</a>
+<a name='L130'></a><a href='#L130'>130</a>
+<a name='L131'></a><a href='#L131'>131</a>
+<a name='L132'></a><a href='#L132'>132</a>
+<a name='L133'></a><a href='#L133'>133</a>
+<a name='L134'></a><a href='#L134'>134</a>
+<a name='L135'></a><a href='#L135'>135</a>
+<a name='L136'></a><a href='#L136'>136</a>
+<a name='L137'></a><a href='#L137'>137</a>
+<a name='L138'></a><a href='#L138'>138</a>
+<a name='L139'></a><a href='#L139'>139</a>
+<a name='L140'></a><a href='#L140'>140</a>
+<a name='L141'></a><a href='#L141'>141</a>
+<a name='L142'></a><a href='#L142'>142</a>
+<a name='L143'></a><a href='#L143'>143</a>
+<a name='L144'></a><a href='#L144'>144</a>
+<a name='L145'></a><a href='#L145'>145</a>
+<a name='L146'></a><a href='#L146'>146</a>
+<a name='L147'></a><a href='#L147'>147</a>
+<a name='L148'></a><a href='#L148'>148</a>
+<a name='L149'></a><a href='#L149'>149</a>
+<a name='L150'></a><a href='#L150'>150</a>
+<a name='L151'></a><a href='#L151'>151</a>
+<a name='L152'></a><a href='#L152'>152</a>
+<a name='L153'></a><a href='#L153'>153</a>
+<a name='L154'></a><a href='#L154'>154</a>
+<a name='L155'></a><a href='#L155'>155</a>
+<a name='L156'></a><a href='#L156'>156</a>
+<a name='L157'></a><a href='#L157'>157</a>
+<a name='L158'></a><a href='#L158'>158</a>
+<a name='L159'></a><a href='#L159'>159</a>
+<a name='L160'></a><a href='#L160'>160</a>
+<a name='L161'></a><a href='#L161'>161</a>
+<a name='L162'></a><a href='#L162'>162</a>
+<a name='L163'></a><a href='#L163'>163</a>
+<a name='L164'></a><a href='#L164'>164</a>
+<a name='L165'></a><a href='#L165'>165</a>
+<a name='L166'></a><a href='#L166'>166</a>
+<a name='L167'></a><a href='#L167'>167</a>
+<a name='L168'></a><a href='#L168'>168</a>
+<a name='L169'></a><a href='#L169'>169</a>
+<a name='L170'></a><a href='#L170'>170</a>
+<a name='L171'></a><a href='#L171'>171</a>
+<a name='L172'></a><a href='#L172'>172</a>
+<a name='L173'></a><a href='#L173'>173</a>
+<a name='L174'></a><a href='#L174'>174</a>
+<a name='L175'></a><a href='#L175'>175</a>
+<a name='L176'></a><a href='#L176'>176</a>
+<a name='L177'></a><a href='#L177'>177</a>
+<a name='L178'></a><a href='#L178'>178</a>
+<a name='L179'></a><a href='#L179'>179</a>
+<a name='L180'></a><a href='#L180'>180</a>
+<a name='L181'></a><a href='#L181'>181</a>
+<a name='L182'></a><a href='#L182'>182</a>
+<a name='L183'></a><a href='#L183'>183</a>
+<a name='L184'></a><a href='#L184'>184</a>
+<a name='L185'></a><a href='#L185'>185</a>
+<a name='L186'></a><a href='#L186'>186</a>
+<a name='L187'></a><a href='#L187'>187</a>
+<a name='L188'></a><a href='#L188'>188</a>
+<a name='L189'></a><a href='#L189'>189</a>
+<a name='L190'></a><a href='#L190'>190</a>
+<a name='L191'></a><a href='#L191'>191</a>
+<a name='L192'></a><a href='#L192'>192</a>
+<a name='L193'></a><a href='#L193'>193</a>
+<a name='L194'></a><a href='#L194'>194</a>
+<a name='L195'></a><a href='#L195'>195</a>
+<a name='L196'></a><a href='#L196'>196</a>
+<a name='L197'></a><a href='#L197'>197</a>
+<a name='L198'></a><a href='#L198'>198</a>
+<a name='L199'></a><a href='#L199'>199</a>
+<a name='L200'></a><a href='#L200'>200</a>
+<a name='L201'></a><a href='#L201'>201</a>
+<a name='L202'></a><a href='#L202'>202</a>
+<a name='L203'></a><a href='#L203'>203</a>
+<a name='L204'></a><a href='#L204'>204</a>
+<a name='L205'></a><a href='#L205'>205</a>
+<a name='L206'></a><a href='#L206'>206</a>
+<a name='L207'></a><a href='#L207'>207</a>
+<a name='L208'></a><a href='#L208'>208</a>
+<a name='L209'></a><a href='#L209'>209</a>
+<a name='L210'></a><a href='#L210'>210</a>
+<a name='L211'></a><a href='#L211'>211</a>
+<a name='L212'></a><a href='#L212'>212</a>
+<a name='L213'></a><a href='#L213'>213</a>
+<a name='L214'></a><a href='#L214'>214</a>
+<a name='L215'></a><a href='#L215'>215</a>
+<a name='L216'></a><a href='#L216'>216</a>
+<a name='L217'></a><a href='#L217'>217</a>
+<a name='L218'></a><a href='#L218'>218</a>
+<a name='L219'></a><a href='#L219'>219</a>
+<a name='L220'></a><a href='#L220'>220</a>
+<a name='L221'></a><a href='#L221'>221</a>
+<a name='L222'></a><a href='#L222'>222</a>
+<a name='L223'></a><a href='#L223'>223</a>
+<a name='L224'></a><a href='#L224'>224</a>
+<a name='L225'></a><a href='#L225'>225</a>
+<a name='L226'></a><a href='#L226'>226</a>
+<a name='L227'></a><a href='#L227'>227</a>
+<a name='L228'></a><a href='#L228'>228</a>
+<a name='L229'></a><a href='#L229'>229</a>
+<a name='L230'></a><a href='#L230'>230</a>
+<a name='L231'></a><a href='#L231'>231</a>
+<a name='L232'></a><a href='#L232'>232</a>
+<a name='L233'></a><a href='#L233'>233</a>
+<a name='L234'></a><a href='#L234'>234</a>
+<a name='L235'></a><a href='#L235'>235</a>
+<a name='L236'></a><a href='#L236'>236</a>
+<a name='L237'></a><a href='#L237'>237</a>
+<a name='L238'></a><a href='#L238'>238</a>
+<a name='L239'></a><a href='#L239'>239</a>
+<a name='L240'></a><a href='#L240'>240</a>
+<a name='L241'></a><a href='#L241'>241</a>
+<a name='L242'></a><a href='#L242'>242</a>
+<a name='L243'></a><a href='#L243'>243</a>
+<a name='L244'></a><a href='#L244'>244</a>
+<a name='L245'></a><a href='#L245'>245</a>
+<a name='L246'></a><a href='#L246'>246</a>
+<a name='L247'></a><a href='#L247'>247</a>
+<a name='L248'></a><a href='#L248'>248</a>
+<a name='L249'></a><a href='#L249'>249</a>
+<a name='L250'></a><a href='#L250'>250</a>
+<a name='L251'></a><a href='#L251'>251</a>
+<a name='L252'></a><a href='#L252'>252</a>
+<a name='L253'></a><a href='#L253'>253</a>
+<a name='L254'></a><a href='#L254'>254</a>
+<a name='L255'></a><a href='#L255'>255</a>
+<a name='L256'></a><a href='#L256'>256</a>
+<a name='L257'></a><a href='#L257'>257</a>
+<a name='L258'></a><a href='#L258'>258</a>
+<a name='L259'></a><a href='#L259'>259</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import { region } from '../config'
+import { getUploadUrl } from '../api'
+import Logger, { LogLevel } from '../logger'
+import * as utils from '../utils'
+&nbsp;
+export const DEFAULT_CHUNK_SIZE = 4 // 单位 MB
+&nbsp;
+/** 上传文件的资源信息配置 */
+export interface Extra {
+  /** 文件原文件名 */
+  fname: string
+  /** 用来放置自定义变量 */
+  customVars?: { [key: string]: string }
+  /** 自定义元信息 */
+  metadata?: { [key: string]: string }
+  /** 文件类型设置 */
+  mimeType?: string //
+}
+&nbsp;
+/** 上传任务的配置信息 */
+export interface Config {
+  /** 是否开启 cdn 加速 */
+  useCdnDomain: boolean
+  /** 是否对分片进行 md5校验 */
+  checkByMD5: boolean
+  /** 强制直传 */
+  forceDirect: boolean
+  /** 上传失败后重试次数 */
+  retryCount: number
+  /** 自定义上传域名 */
+  uphost: string
+  /** 自定义分片上传并发请求量 */
+  concurrentRequestLimit: number
+  /** 分片大小,单位为 MB */
+  chunkSize: number
+  /** 上传域名协议 */
+  upprotocol: 'http:' | 'https:'
+  /** 上传区域 */
+  region?: typeof region[keyof typeof region]
+  /** 是否禁止统计日志上报 */
+  disableStatisticsReport: boolean
+  /** 设置调试日志输出模式,默认 `OFF`,不输出任何日志 */
+  debugLogLevel?: LogLevel
+}
+&nbsp;
+export interface UploadOptions {
+  file: File
+  key: string | null | undefined
+  token: string
+  putExtra?: Partial&lt;Extra&gt;
+  config?: Partial&lt;Config&gt;
+}
+&nbsp;
+export interface UploadInfo {
+  id: string
+  url: string
+}
+&nbsp;
+/** 传递给外部的上传进度信息 */
+export interface UploadProgress {
+  total: ProgressCompose
+  uploadInfo?: UploadInfo
+  chunks?: ProgressCompose[]
+}
+&nbsp;
+export interface UploadHandler {
+  onData: (data: UploadProgress) =&gt; void
+  onError: (err: utils.CustomError) =&gt; void
+  onComplete: (res: any) =&gt; void
+}
+&nbsp;
+export interface Progress {
+  loaded: number
+  total: number
+}
+&nbsp;
+export interface ProgressCompose {
+  loaded: number
+  size: number
+  percent: number
+}
+&nbsp;
+export type XHRHandler = (xhr: XMLHttpRequest) =&gt; void
+&nbsp;
+const GB = 1024 ** 3
+&nbsp;
+export default abstract class Base {
+  protected config: Config
+  protected putExtra: Extra
+  protected <span class="cstat-no" title="statement not covered" >xhrList: XMLHttpRequest[] = []</span>
+  protected file: File
+  protected key: string | null | undefined
+  protected <span class="cstat-no" title="statement not covered" >aborted = false</span>
+  protected <span class="cstat-no" title="statement not covered" >retryCount = 0</span>
+  protected token: string
+  protected uploadUrl: string
+  protected bucket: string
+  protected uploadAt: number
+  protected progress: UploadProgress
+&nbsp;
+  protected onData: (data: UploadProgress) =&gt; void
+  protected onError: (err: utils.CustomError) =&gt; void
+  protected onComplete: (res: any) =&gt; void
+&nbsp;
+  protected abstract run(): utils.Response&lt;any&gt;
+&nbsp;
+<span class="fstat-no" title="function not covered" >  constructor(o</span>ptions: UploadOptions, handlers: UploadHandler, protected <span class="cstat-no" title="statement not covered" >logger: Logger)</span> {
+<span class="cstat-no" title="statement not covered" >    this.config = {</span>
+      useCdnDomain: true,
+      disableStatisticsReport: false,
+      retryCount: 3,
+      checkByMD5: false,
+      uphost: '',
+      upprotocol: 'https:',
+      forceDirect: false,
+      chunkSize: DEFAULT_CHUNK_SIZE,
+      concurrentRequestLimit: 3,
+      ...options.config
+    }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    logger.info('config inited.', this.config)</span>
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    this.putExtra = {</span>
+      fname: '',
+      ...options.putExtra
+    }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    logger.info('putExtra inited.', this.putExtra)</span>
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    this.file = options.file</span>
+<span class="cstat-no" title="statement not covered" >    this.key = options.key</span>
+<span class="cstat-no" title="statement not covered" >    this.token = options.token</span>
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    this.onData = handlers.onData</span>
+<span class="cstat-no" title="statement not covered" >    this.onError = handlers.onError</span>
+<span class="cstat-no" title="statement not covered" >    this.onComplete = handlers.onComplete</span>
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    try {</span>
+<span class="cstat-no" title="statement not covered" >      this.bucket = utils.getPutPolicy(this.token).bucket</span>
+    } catch (e) {
+<span class="cstat-no" title="statement not covered" >      logger.error('get bucket from token failed.', e)</span>
+<span class="cstat-no" title="statement not covered" >      this.onError(e)</span>
+    }
+  }
+&nbsp;
+<span class="fstat-no" title="function not covered" >  private h</span>andleError(message: string) {
+    const err = <span class="cstat-no" title="statement not covered" >new Error(message)</span>
+<span class="cstat-no" title="statement not covered" >    this.logger.error(message)</span>
+<span class="cstat-no" title="statement not covered" >    this.onError(err)</span>
+  }
+&nbsp;
+  /**
+   * @returns Promise 返回结果与上传最终状态无关,状态信息请通过 [Subscriber] 获取。
+   * @description 上传文件,状态信息请通过 [Subscriber] 获取。
+   */
+<span class="fstat-no" title="function not covered" >  public async p</span>utFile(): Promise&lt;void&gt; {
+<span class="cstat-no" title="statement not covered" >    this.aborted = false</span>
+<span class="cstat-no" title="statement not covered" >    if (!this.putExtra.fname) {</span>
+<span class="cstat-no" title="statement not covered" >      this.logger.info('use file.name as fname.')</span>
+<span class="cstat-no" title="statement not covered" >      this.putExtra.fname = this.file.name</span>
+    }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    if (this.file.size &gt; 10000 * GB) {</span>
+<span class="cstat-no" title="statement not covered" >      this.handleError('file size exceed maximum value 10000G.')</span>
+<span class="cstat-no" title="statement not covered" >      return</span>
+    }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    if (this.putExtra.customVars) {</span>
+<span class="cstat-no" title="statement not covered" >      if (!utils.isCustomVarsValid(this.putExtra.customVars)) {</span>
+<span class="cstat-no" title="statement not covered" >        this.handleError('customVars key should start width x:.')</span>
+<span class="cstat-no" title="statement not covered" >        return</span>
+      }
+    }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    if (this.putExtra.metadata) {</span>
+<span class="cstat-no" title="statement not covered" >      if (!utils.isMetaDataValid(this.putExtra.metadata)) {</span>
+<span class="cstat-no" title="statement not covered" >        this.handleError('metadata key should start with x-qn-meta-.')</span>
+<span class="cstat-no" title="statement not covered" >        return</span>
+      }
+    }
+&nbsp;
+    try {
+<span class="cstat-no" title="statement not covered" ><span class="cstat-no" title="statement not covered" >      this.u</span>ploadUrl = <span class="cstat-no" title="statement not covered" >await getUploadUrl(this.config, this.token)</span></span>
+<span class="cstat-no" title="statement not covered" >      this.logger.info('get uploadUrl from api.', this.uploadUrl)</span>
+<span class="cstat-no" title="statement not covered" >      this.uploadAt = new Date().getTime()</span>
+&nbsp;
+      const <span class="cstat-no" title="statement not covered" >result = <span class="cstat-no" title="statement not covered" >await this.run()</span></span>
+<span class="cstat-no" title="statement not covered" >      this.onComplete(result.data)</span>
+<span class="cstat-no" title="statement not covered" >      this.sendLog(result.reqId, 200)</span>
+<span class="cstat-no" title="statement not covered" >      return</span>
+    } catch (err) {
+<span class="cstat-no" title="statement not covered" >      this.logger.error(err)</span>
+&nbsp;
+<span class="cstat-no" title="statement not covered" >      this.clear()</span>
+<span class="cstat-no" title="statement not covered" >      if (err.isRequestError) {</span>
+        const <span class="cstat-no" title="statement not covered" >reqId = this.aborted ? '' : err.reqId</span>
+        const <span class="cstat-no" title="statement not covered" >code = this.aborted ? -2 : err.code</span>
+<span class="cstat-no" title="statement not covered" >        this.sendLog(reqId, code)</span>
+      }
+&nbsp;
+      const <span class="cstat-no" title="statement not covered" >needRetry = err.isRequestError &amp;&amp; err.code === 0 &amp;&amp; !this.aborted</span>
+      const <span class="cstat-no" title="statement not covered" >notReachRetryCount = ++this.retryCount &lt;= this.config.retryCount</span>
+      // 以下条件满足其中之一则会进行重新上传:
+      // 1. 满足 needRetry 的条件且 retryCount 不为 0
+      // 2. uploadId 无效时在 resume 里会清除本地数据,并且这里触发重新上传
+<span class="cstat-no" title="statement not covered" >      if (needRetry &amp;&amp; notReachRetryCount || err.code === 612) {</span>
+<span class="cstat-no" title="statement not covered" >        this.logger.warn(`error auto retry: ${this.retryCount}/${this.config.retryCount}.`)</span>
+<span class="cstat-no" title="statement not covered" >        this.putFile()</span>
+<span class="cstat-no" title="statement not covered" >        return</span>
+      }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >      this.onError(err)</span>
+    }
+  }
+&nbsp;
+<span class="fstat-no" title="function not covered" >  private c</span>lear() {
+<span class="cstat-no" title="statement not covered" >    this.logger.info('start cleaning all xhr.')</span>
+<span class="cstat-no" title="statement not covered" >    this.xhrList.forEach(<span class="fstat-no" title="function not covered" >xhr </span>=&gt; {</span>
+<span class="cstat-no" title="statement not covered" >      xhr.onreadystatechange = null</span>
+<span class="cstat-no" title="statement not covered" >      xhr.abort()</span>
+    })
+<span class="cstat-no" title="statement not covered" >    this.logger.info('cleanup completed.')</span>
+<span class="cstat-no" title="statement not covered" >    this.xhrList = []</span>
+  }
+&nbsp;
+<span class="fstat-no" title="function not covered" >  public s</span>top() {
+<span class="cstat-no" title="statement not covered" >    this.logger.info('stop.')</span>
+<span class="cstat-no" title="statement not covered" >    this.clear()</span>
+<span class="cstat-no" title="statement not covered" >    this.aborted = true</span>
+  }
+&nbsp;
+<span class="fstat-no" title="function not covered" >  public a</span>ddXhr(xhr: XMLHttpRequest) {
+<span class="cstat-no" title="statement not covered" >    this.xhrList.push(xhr)</span>
+  }
+&nbsp;
+<span class="fstat-no" title="function not covered" >  private s</span>endLog(reqId: string, code: number) {
+<span class="cstat-no" title="statement not covered" >    this.logger.report({</span>
+      code,
+      reqId,
+      host: utils.getDomainFromUrl(this.uploadUrl),
+      remoteIp: '',
+      port: utils.getPortFromUrl(this.uploadUrl),
+      duration: (new Date().getTime() - this.uploadAt) / 1000,
+      time: Math.floor(this.uploadAt / 1000),
+      bytesSent: this.progress ? this.progress.total.loaded : 0,
+      upType: 'jssdk-h5',
+      size: this.file.size
+    })
+  }
+&nbsp;
+<span class="fstat-no" title="function not covered" >  public g</span>etProgressInfoItem(loaded: number, size: number) {
+<span class="cstat-no" title="statement not covered" >    return {</span>
+      loaded,
+      size,
+      percent: loaded / size * 100
+    }
+  }
+}
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Thu Apr 22 2021 01:38:54 GMT+0000 (Coordinated Universal Time)
+            </div>
+        </div>
+        <script src="../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../sorter.js"></script>
+        <script src="../../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 111 - 0
qiniu-js/coverage/lcov-report/src/upload/index.html

@@ -0,0 +1,111 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for src/upload</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../prettify.css" />
+    <link rel="stylesheet" href="../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../index.html">All files</a> src/upload</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">15.48% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>13/84</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">0% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>0/28</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">10% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>1/10</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">16.05% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>13/81</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line low'></div>
+    <div class="pad1">
+<table class="coverage-summary">
+<thead>
+<tr>
+   <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
+   <th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
+   <th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
+   <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
+   <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
+   <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
+   <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
+</tr>
+</thead>
+<tbody><tr>
+	<td class="file low" data-value="base.ts"><a href="base.ts.html">base.ts</a></td>
+	<td data-value="15.48" class="pic low">
+	<div class="chart"><div class="cover-fill" style="width: 15%"></div><div class="cover-empty" style="width: 85%"></div></div>
+	</td>
+	<td data-value="15.48" class="pct low">15.48%</td>
+	<td data-value="84" class="abs low">13/84</td>
+	<td data-value="0" class="pct low">0%</td>
+	<td data-value="28" class="abs low">0/28</td>
+	<td data-value="10" class="pct low">10%</td>
+	<td data-value="10" class="abs low">1/10</td>
+	<td data-value="16.05" class="pct low">16.05%</td>
+	<td data-value="81" class="abs low">13/81</td>
+	</tr>
+
+</tbody>
+</table>
+</div>
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Thu Apr 22 2021 01:38:54 GMT+0000 (Coordinated Universal Time)
+            </div>
+        </div>
+        <script src="../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../sorter.js"></script>
+        <script src="../../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 1142 - 0
qiniu-js/coverage/lcov-report/src/utils.ts.html

@@ -0,0 +1,1142 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for src/utils.ts</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../prettify.css" />
+    <link rel="stylesheet" href="../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../index.html">All files</a> / <a href="index.html">src</a> utils.ts</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">17.24% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>25/145</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">3.39% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>2/59</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">3.33% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>1/30</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">18.12% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>25/138</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line low'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a>
+<a name='L17'></a><a href='#L17'>17</a>
+<a name='L18'></a><a href='#L18'>18</a>
+<a name='L19'></a><a href='#L19'>19</a>
+<a name='L20'></a><a href='#L20'>20</a>
+<a name='L21'></a><a href='#L21'>21</a>
+<a name='L22'></a><a href='#L22'>22</a>
+<a name='L23'></a><a href='#L23'>23</a>
+<a name='L24'></a><a href='#L24'>24</a>
+<a name='L25'></a><a href='#L25'>25</a>
+<a name='L26'></a><a href='#L26'>26</a>
+<a name='L27'></a><a href='#L27'>27</a>
+<a name='L28'></a><a href='#L28'>28</a>
+<a name='L29'></a><a href='#L29'>29</a>
+<a name='L30'></a><a href='#L30'>30</a>
+<a name='L31'></a><a href='#L31'>31</a>
+<a name='L32'></a><a href='#L32'>32</a>
+<a name='L33'></a><a href='#L33'>33</a>
+<a name='L34'></a><a href='#L34'>34</a>
+<a name='L35'></a><a href='#L35'>35</a>
+<a name='L36'></a><a href='#L36'>36</a>
+<a name='L37'></a><a href='#L37'>37</a>
+<a name='L38'></a><a href='#L38'>38</a>
+<a name='L39'></a><a href='#L39'>39</a>
+<a name='L40'></a><a href='#L40'>40</a>
+<a name='L41'></a><a href='#L41'>41</a>
+<a name='L42'></a><a href='#L42'>42</a>
+<a name='L43'></a><a href='#L43'>43</a>
+<a name='L44'></a><a href='#L44'>44</a>
+<a name='L45'></a><a href='#L45'>45</a>
+<a name='L46'></a><a href='#L46'>46</a>
+<a name='L47'></a><a href='#L47'>47</a>
+<a name='L48'></a><a href='#L48'>48</a>
+<a name='L49'></a><a href='#L49'>49</a>
+<a name='L50'></a><a href='#L50'>50</a>
+<a name='L51'></a><a href='#L51'>51</a>
+<a name='L52'></a><a href='#L52'>52</a>
+<a name='L53'></a><a href='#L53'>53</a>
+<a name='L54'></a><a href='#L54'>54</a>
+<a name='L55'></a><a href='#L55'>55</a>
+<a name='L56'></a><a href='#L56'>56</a>
+<a name='L57'></a><a href='#L57'>57</a>
+<a name='L58'></a><a href='#L58'>58</a>
+<a name='L59'></a><a href='#L59'>59</a>
+<a name='L60'></a><a href='#L60'>60</a>
+<a name='L61'></a><a href='#L61'>61</a>
+<a name='L62'></a><a href='#L62'>62</a>
+<a name='L63'></a><a href='#L63'>63</a>
+<a name='L64'></a><a href='#L64'>64</a>
+<a name='L65'></a><a href='#L65'>65</a>
+<a name='L66'></a><a href='#L66'>66</a>
+<a name='L67'></a><a href='#L67'>67</a>
+<a name='L68'></a><a href='#L68'>68</a>
+<a name='L69'></a><a href='#L69'>69</a>
+<a name='L70'></a><a href='#L70'>70</a>
+<a name='L71'></a><a href='#L71'>71</a>
+<a name='L72'></a><a href='#L72'>72</a>
+<a name='L73'></a><a href='#L73'>73</a>
+<a name='L74'></a><a href='#L74'>74</a>
+<a name='L75'></a><a href='#L75'>75</a>
+<a name='L76'></a><a href='#L76'>76</a>
+<a name='L77'></a><a href='#L77'>77</a>
+<a name='L78'></a><a href='#L78'>78</a>
+<a name='L79'></a><a href='#L79'>79</a>
+<a name='L80'></a><a href='#L80'>80</a>
+<a name='L81'></a><a href='#L81'>81</a>
+<a name='L82'></a><a href='#L82'>82</a>
+<a name='L83'></a><a href='#L83'>83</a>
+<a name='L84'></a><a href='#L84'>84</a>
+<a name='L85'></a><a href='#L85'>85</a>
+<a name='L86'></a><a href='#L86'>86</a>
+<a name='L87'></a><a href='#L87'>87</a>
+<a name='L88'></a><a href='#L88'>88</a>
+<a name='L89'></a><a href='#L89'>89</a>
+<a name='L90'></a><a href='#L90'>90</a>
+<a name='L91'></a><a href='#L91'>91</a>
+<a name='L92'></a><a href='#L92'>92</a>
+<a name='L93'></a><a href='#L93'>93</a>
+<a name='L94'></a><a href='#L94'>94</a>
+<a name='L95'></a><a href='#L95'>95</a>
+<a name='L96'></a><a href='#L96'>96</a>
+<a name='L97'></a><a href='#L97'>97</a>
+<a name='L98'></a><a href='#L98'>98</a>
+<a name='L99'></a><a href='#L99'>99</a>
+<a name='L100'></a><a href='#L100'>100</a>
+<a name='L101'></a><a href='#L101'>101</a>
+<a name='L102'></a><a href='#L102'>102</a>
+<a name='L103'></a><a href='#L103'>103</a>
+<a name='L104'></a><a href='#L104'>104</a>
+<a name='L105'></a><a href='#L105'>105</a>
+<a name='L106'></a><a href='#L106'>106</a>
+<a name='L107'></a><a href='#L107'>107</a>
+<a name='L108'></a><a href='#L108'>108</a>
+<a name='L109'></a><a href='#L109'>109</a>
+<a name='L110'></a><a href='#L110'>110</a>
+<a name='L111'></a><a href='#L111'>111</a>
+<a name='L112'></a><a href='#L112'>112</a>
+<a name='L113'></a><a href='#L113'>113</a>
+<a name='L114'></a><a href='#L114'>114</a>
+<a name='L115'></a><a href='#L115'>115</a>
+<a name='L116'></a><a href='#L116'>116</a>
+<a name='L117'></a><a href='#L117'>117</a>
+<a name='L118'></a><a href='#L118'>118</a>
+<a name='L119'></a><a href='#L119'>119</a>
+<a name='L120'></a><a href='#L120'>120</a>
+<a name='L121'></a><a href='#L121'>121</a>
+<a name='L122'></a><a href='#L122'>122</a>
+<a name='L123'></a><a href='#L123'>123</a>
+<a name='L124'></a><a href='#L124'>124</a>
+<a name='L125'></a><a href='#L125'>125</a>
+<a name='L126'></a><a href='#L126'>126</a>
+<a name='L127'></a><a href='#L127'>127</a>
+<a name='L128'></a><a href='#L128'>128</a>
+<a name='L129'></a><a href='#L129'>129</a>
+<a name='L130'></a><a href='#L130'>130</a>
+<a name='L131'></a><a href='#L131'>131</a>
+<a name='L132'></a><a href='#L132'>132</a>
+<a name='L133'></a><a href='#L133'>133</a>
+<a name='L134'></a><a href='#L134'>134</a>
+<a name='L135'></a><a href='#L135'>135</a>
+<a name='L136'></a><a href='#L136'>136</a>
+<a name='L137'></a><a href='#L137'>137</a>
+<a name='L138'></a><a href='#L138'>138</a>
+<a name='L139'></a><a href='#L139'>139</a>
+<a name='L140'></a><a href='#L140'>140</a>
+<a name='L141'></a><a href='#L141'>141</a>
+<a name='L142'></a><a href='#L142'>142</a>
+<a name='L143'></a><a href='#L143'>143</a>
+<a name='L144'></a><a href='#L144'>144</a>
+<a name='L145'></a><a href='#L145'>145</a>
+<a name='L146'></a><a href='#L146'>146</a>
+<a name='L147'></a><a href='#L147'>147</a>
+<a name='L148'></a><a href='#L148'>148</a>
+<a name='L149'></a><a href='#L149'>149</a>
+<a name='L150'></a><a href='#L150'>150</a>
+<a name='L151'></a><a href='#L151'>151</a>
+<a name='L152'></a><a href='#L152'>152</a>
+<a name='L153'></a><a href='#L153'>153</a>
+<a name='L154'></a><a href='#L154'>154</a>
+<a name='L155'></a><a href='#L155'>155</a>
+<a name='L156'></a><a href='#L156'>156</a>
+<a name='L157'></a><a href='#L157'>157</a>
+<a name='L158'></a><a href='#L158'>158</a>
+<a name='L159'></a><a href='#L159'>159</a>
+<a name='L160'></a><a href='#L160'>160</a>
+<a name='L161'></a><a href='#L161'>161</a>
+<a name='L162'></a><a href='#L162'>162</a>
+<a name='L163'></a><a href='#L163'>163</a>
+<a name='L164'></a><a href='#L164'>164</a>
+<a name='L165'></a><a href='#L165'>165</a>
+<a name='L166'></a><a href='#L166'>166</a>
+<a name='L167'></a><a href='#L167'>167</a>
+<a name='L168'></a><a href='#L168'>168</a>
+<a name='L169'></a><a href='#L169'>169</a>
+<a name='L170'></a><a href='#L170'>170</a>
+<a name='L171'></a><a href='#L171'>171</a>
+<a name='L172'></a><a href='#L172'>172</a>
+<a name='L173'></a><a href='#L173'>173</a>
+<a name='L174'></a><a href='#L174'>174</a>
+<a name='L175'></a><a href='#L175'>175</a>
+<a name='L176'></a><a href='#L176'>176</a>
+<a name='L177'></a><a href='#L177'>177</a>
+<a name='L178'></a><a href='#L178'>178</a>
+<a name='L179'></a><a href='#L179'>179</a>
+<a name='L180'></a><a href='#L180'>180</a>
+<a name='L181'></a><a href='#L181'>181</a>
+<a name='L182'></a><a href='#L182'>182</a>
+<a name='L183'></a><a href='#L183'>183</a>
+<a name='L184'></a><a href='#L184'>184</a>
+<a name='L185'></a><a href='#L185'>185</a>
+<a name='L186'></a><a href='#L186'>186</a>
+<a name='L187'></a><a href='#L187'>187</a>
+<a name='L188'></a><a href='#L188'>188</a>
+<a name='L189'></a><a href='#L189'>189</a>
+<a name='L190'></a><a href='#L190'>190</a>
+<a name='L191'></a><a href='#L191'>191</a>
+<a name='L192'></a><a href='#L192'>192</a>
+<a name='L193'></a><a href='#L193'>193</a>
+<a name='L194'></a><a href='#L194'>194</a>
+<a name='L195'></a><a href='#L195'>195</a>
+<a name='L196'></a><a href='#L196'>196</a>
+<a name='L197'></a><a href='#L197'>197</a>
+<a name='L198'></a><a href='#L198'>198</a>
+<a name='L199'></a><a href='#L199'>199</a>
+<a name='L200'></a><a href='#L200'>200</a>
+<a name='L201'></a><a href='#L201'>201</a>
+<a name='L202'></a><a href='#L202'>202</a>
+<a name='L203'></a><a href='#L203'>203</a>
+<a name='L204'></a><a href='#L204'>204</a>
+<a name='L205'></a><a href='#L205'>205</a>
+<a name='L206'></a><a href='#L206'>206</a>
+<a name='L207'></a><a href='#L207'>207</a>
+<a name='L208'></a><a href='#L208'>208</a>
+<a name='L209'></a><a href='#L209'>209</a>
+<a name='L210'></a><a href='#L210'>210</a>
+<a name='L211'></a><a href='#L211'>211</a>
+<a name='L212'></a><a href='#L212'>212</a>
+<a name='L213'></a><a href='#L213'>213</a>
+<a name='L214'></a><a href='#L214'>214</a>
+<a name='L215'></a><a href='#L215'>215</a>
+<a name='L216'></a><a href='#L216'>216</a>
+<a name='L217'></a><a href='#L217'>217</a>
+<a name='L218'></a><a href='#L218'>218</a>
+<a name='L219'></a><a href='#L219'>219</a>
+<a name='L220'></a><a href='#L220'>220</a>
+<a name='L221'></a><a href='#L221'>221</a>
+<a name='L222'></a><a href='#L222'>222</a>
+<a name='L223'></a><a href='#L223'>223</a>
+<a name='L224'></a><a href='#L224'>224</a>
+<a name='L225'></a><a href='#L225'>225</a>
+<a name='L226'></a><a href='#L226'>226</a>
+<a name='L227'></a><a href='#L227'>227</a>
+<a name='L228'></a><a href='#L228'>228</a>
+<a name='L229'></a><a href='#L229'>229</a>
+<a name='L230'></a><a href='#L230'>230</a>
+<a name='L231'></a><a href='#L231'>231</a>
+<a name='L232'></a><a href='#L232'>232</a>
+<a name='L233'></a><a href='#L233'>233</a>
+<a name='L234'></a><a href='#L234'>234</a>
+<a name='L235'></a><a href='#L235'>235</a>
+<a name='L236'></a><a href='#L236'>236</a>
+<a name='L237'></a><a href='#L237'>237</a>
+<a name='L238'></a><a href='#L238'>238</a>
+<a name='L239'></a><a href='#L239'>239</a>
+<a name='L240'></a><a href='#L240'>240</a>
+<a name='L241'></a><a href='#L241'>241</a>
+<a name='L242'></a><a href='#L242'>242</a>
+<a name='L243'></a><a href='#L243'>243</a>
+<a name='L244'></a><a href='#L244'>244</a>
+<a name='L245'></a><a href='#L245'>245</a>
+<a name='L246'></a><a href='#L246'>246</a>
+<a name='L247'></a><a href='#L247'>247</a>
+<a name='L248'></a><a href='#L248'>248</a>
+<a name='L249'></a><a href='#L249'>249</a>
+<a name='L250'></a><a href='#L250'>250</a>
+<a name='L251'></a><a href='#L251'>251</a>
+<a name='L252'></a><a href='#L252'>252</a>
+<a name='L253'></a><a href='#L253'>253</a>
+<a name='L254'></a><a href='#L254'>254</a>
+<a name='L255'></a><a href='#L255'>255</a>
+<a name='L256'></a><a href='#L256'>256</a>
+<a name='L257'></a><a href='#L257'>257</a>
+<a name='L258'></a><a href='#L258'>258</a>
+<a name='L259'></a><a href='#L259'>259</a>
+<a name='L260'></a><a href='#L260'>260</a>
+<a name='L261'></a><a href='#L261'>261</a>
+<a name='L262'></a><a href='#L262'>262</a>
+<a name='L263'></a><a href='#L263'>263</a>
+<a name='L264'></a><a href='#L264'>264</a>
+<a name='L265'></a><a href='#L265'>265</a>
+<a name='L266'></a><a href='#L266'>266</a>
+<a name='L267'></a><a href='#L267'>267</a>
+<a name='L268'></a><a href='#L268'>268</a>
+<a name='L269'></a><a href='#L269'>269</a>
+<a name='L270'></a><a href='#L270'>270</a>
+<a name='L271'></a><a href='#L271'>271</a>
+<a name='L272'></a><a href='#L272'>272</a>
+<a name='L273'></a><a href='#L273'>273</a>
+<a name='L274'></a><a href='#L274'>274</a>
+<a name='L275'></a><a href='#L275'>275</a>
+<a name='L276'></a><a href='#L276'>276</a>
+<a name='L277'></a><a href='#L277'>277</a>
+<a name='L278'></a><a href='#L278'>278</a>
+<a name='L279'></a><a href='#L279'>279</a>
+<a name='L280'></a><a href='#L280'>280</a>
+<a name='L281'></a><a href='#L281'>281</a>
+<a name='L282'></a><a href='#L282'>282</a>
+<a name='L283'></a><a href='#L283'>283</a>
+<a name='L284'></a><a href='#L284'>284</a>
+<a name='L285'></a><a href='#L285'>285</a>
+<a name='L286'></a><a href='#L286'>286</a>
+<a name='L287'></a><a href='#L287'>287</a>
+<a name='L288'></a><a href='#L288'>288</a>
+<a name='L289'></a><a href='#L289'>289</a>
+<a name='L290'></a><a href='#L290'>290</a>
+<a name='L291'></a><a href='#L291'>291</a>
+<a name='L292'></a><a href='#L292'>292</a>
+<a name='L293'></a><a href='#L293'>293</a>
+<a name='L294'></a><a href='#L294'>294</a>
+<a name='L295'></a><a href='#L295'>295</a>
+<a name='L296'></a><a href='#L296'>296</a>
+<a name='L297'></a><a href='#L297'>297</a>
+<a name='L298'></a><a href='#L298'>298</a>
+<a name='L299'></a><a href='#L299'>299</a>
+<a name='L300'></a><a href='#L300'>300</a>
+<a name='L301'></a><a href='#L301'>301</a>
+<a name='L302'></a><a href='#L302'>302</a>
+<a name='L303'></a><a href='#L303'>303</a>
+<a name='L304'></a><a href='#L304'>304</a>
+<a name='L305'></a><a href='#L305'>305</a>
+<a name='L306'></a><a href='#L306'>306</a>
+<a name='L307'></a><a href='#L307'>307</a>
+<a name='L308'></a><a href='#L308'>308</a>
+<a name='L309'></a><a href='#L309'>309</a>
+<a name='L310'></a><a href='#L310'>310</a>
+<a name='L311'></a><a href='#L311'>311</a>
+<a name='L312'></a><a href='#L312'>312</a>
+<a name='L313'></a><a href='#L313'>313</a>
+<a name='L314'></a><a href='#L314'>314</a>
+<a name='L315'></a><a href='#L315'>315</a>
+<a name='L316'></a><a href='#L316'>316</a>
+<a name='L317'></a><a href='#L317'>317</a>
+<a name='L318'></a><a href='#L318'>318</a>
+<a name='L319'></a><a href='#L319'>319</a>
+<a name='L320'></a><a href='#L320'>320</a>
+<a name='L321'></a><a href='#L321'>321</a>
+<a name='L322'></a><a href='#L322'>322</a>
+<a name='L323'></a><a href='#L323'>323</a>
+<a name='L324'></a><a href='#L324'>324</a>
+<a name='L325'></a><a href='#L325'>325</a>
+<a name='L326'></a><a href='#L326'>326</a>
+<a name='L327'></a><a href='#L327'>327</a>
+<a name='L328'></a><a href='#L328'>328</a>
+<a name='L329'></a><a href='#L329'>329</a>
+<a name='L330'></a><a href='#L330'>330</a>
+<a name='L331'></a><a href='#L331'>331</a>
+<a name='L332'></a><a href='#L332'>332</a>
+<a name='L333'></a><a href='#L333'>333</a>
+<a name='L334'></a><a href='#L334'>334</a>
+<a name='L335'></a><a href='#L335'>335</a>
+<a name='L336'></a><a href='#L336'>336</a>
+<a name='L337'></a><a href='#L337'>337</a>
+<a name='L338'></a><a href='#L338'>338</a>
+<a name='L339'></a><a href='#L339'>339</a>
+<a name='L340'></a><a href='#L340'>340</a>
+<a name='L341'></a><a href='#L341'>341</a>
+<a name='L342'></a><a href='#L342'>342</a>
+<a name='L343'></a><a href='#L343'>343</a>
+<a name='L344'></a><a href='#L344'>344</a>
+<a name='L345'></a><a href='#L345'>345</a>
+<a name='L346'></a><a href='#L346'>346</a>
+<a name='L347'></a><a href='#L347'>347</a>
+<a name='L348'></a><a href='#L348'>348</a>
+<a name='L349'></a><a href='#L349'>349</a>
+<a name='L350'></a><a href='#L350'>350</a>
+<a name='L351'></a><a href='#L351'>351</a>
+<a name='L352'></a><a href='#L352'>352</a>
+<a name='L353'></a><a href='#L353'>353</a>
+<a name='L354'></a><a href='#L354'>354</a>
+<a name='L355'></a><a href='#L355'>355</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import SparkMD5 from 'spark-md5'
+import { Progress, LocalInfo } from './upload'
+import { urlSafeBase64Decode } from './base64'
+&nbsp;
+export const MB = 1024 ** 2
+&nbsp;
+// 文件分块
+export function <span class="fstat-no" title="function not covered" >getChunks(</span>file: File, blockSize: number): Blob[] {
+&nbsp;
+  let chunkByteSize = <span class="cstat-no" title="statement not covered" >blockSize * MB </span>// 转换为字节
+  // 如果 chunkByteSize 比文件大,则直接取文件的大小
+<span class="cstat-no" title="statement not covered" >  if (chunkByteSize &gt; file.size) {</span>
+<span class="cstat-no" title="statement not covered" >    chunkByteSize = file.size</span>
+  } else {
+    // 因为最多 10000 chunk,所以如果 chunkSize 不符合则把每片 chunk 大小扩大两倍
+<span class="cstat-no" title="statement not covered" >    while (file.size &gt; chunkByteSize * 10000) {</span>
+<span class="cstat-no" title="statement not covered" >      chunkByteSize *= 2</span>
+    }
+  }
+&nbsp;
+  const chunks: Blob[] = <span class="cstat-no" title="statement not covered" >[]</span>
+  const count = <span class="cstat-no" title="statement not covered" >Math.ceil(file.size / chunkByteSize)</span>
+<span class="cstat-no" title="statement not covered" >  for (let i = <span class="cstat-no" title="statement not covered" >0;</span> i &lt; count; i++) {</span>
+    const chunk = <span class="cstat-no" title="statement not covered" >file.slice(</span>
+      chunkByteSize * i,
+      i === count - 1 ? file.size : chunkByteSize * (i + 1)
+    )
+<span class="cstat-no" title="statement not covered" >    chunks.push(chunk)</span>
+  }
+<span class="cstat-no" title="statement not covered" >  return chunks</span>
+}
+&nbsp;
+export function <span class="fstat-no" title="function not covered" >isMetaDataValid(</span>params: { [key: string]: string }) {
+<span class="cstat-no" title="statement not covered" >  return Object.keys(params).every(<span class="fstat-no" title="function not covered" >key </span>=&gt; <span class="cstat-no" title="statement not covered" >key.indexOf('x-qn-meta-') === 0)</span></span>
+}
+&nbsp;
+export function <span class="fstat-no" title="function not covered" >isCustomVarsValid(</span>params: { [key: string]: string }) {
+<span class="cstat-no" title="statement not covered" >  return Object.keys(params).every(<span class="fstat-no" title="function not covered" >key </span>=&gt; <span class="cstat-no" title="statement not covered" >key.indexOf('x:') === 0)</span></span>
+}
+&nbsp;
+export function <span class="fstat-no" title="function not covered" >sum(</span>list: number[]) {
+<span class="cstat-no" title="statement not covered" >  return list.reduce(<span class="fstat-no" title="function not covered" >(d</span>ata, loaded) =&gt; <span class="cstat-no" title="statement not covered" >data + loaded, 0</span>)</span>
+}
+&nbsp;
+export function <span class="fstat-no" title="function not covered" >setLocalFileInfo(</span>localKey: string, info: LocalInfo) {
+<span class="cstat-no" title="statement not covered" >  try {</span>
+<span class="cstat-no" title="statement not covered" >    localStorage.setItem(localKey, JSON.stringify(info))</span>
+  } catch (err) {
+<span class="cstat-no" title="statement not covered" >    throw new Error(`setLocalFileInfo failed: ${localKey}`)</span>
+  }
+}
+&nbsp;
+export function createLocalKey(name: string, key: string | null | undefined, size: number): string {
+  const localKey = key == null ? '_' : `_key_${key}_`
+  return `qiniu_js_sdk_upload_file_name_${name}${localKey}size_${size}`
+}
+&nbsp;
+export function <span class="fstat-no" title="function not covered" >removeLocalFileInfo(</span>localKey: string) {
+<span class="cstat-no" title="statement not covered" >  try {</span>
+<span class="cstat-no" title="statement not covered" >    localStorage.removeItem(localKey)</span>
+  } catch (err) {
+<span class="cstat-no" title="statement not covered" >    throw new Error(`removeLocalFileInfo failed. key: ${localKey}`)</span>
+  }
+}
+&nbsp;
+export function <span class="fstat-no" title="function not covered" >getLocalFileInfo(</span>localKey: string): LocalInfo | null {
+  let localInfoString: string | null = <span class="cstat-no" title="statement not covered" >null</span>
+<span class="cstat-no" title="statement not covered" >  try { <span class="cstat-no" title="statement not covered" >localInfoString = localStorage.getItem(localKey) }</span></span>
+  catch { <span class="cstat-no" title="statement not covered" >throw new Error(`getLocalFileInfo failed. key: ${localKey}`) }</span>
+&nbsp;
+<span class="cstat-no" title="statement not covered" >  if (localInfoString == null) {</span>
+<span class="cstat-no" title="statement not covered" >    return null</span>
+  }
+&nbsp;
+  let localInfo: LocalInfo | null = <span class="cstat-no" title="statement not covered" >null</span>
+<span class="cstat-no" title="statement not covered" >  try { <span class="cstat-no" title="statement not covered" >localInfo = JSON.parse(localInfoString) }</span></span>
+  catch {
+    // 本地信息已被破坏,直接删除
+<span class="cstat-no" title="statement not covered" >    removeLocalFileInfo(localKey)</span>
+<span class="cstat-no" title="statement not covered" >    throw new Error(`getLocalFileInfo failed to parse. key: ${localKey}`)</span>
+  }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >  return localInfo</span>
+}
+&nbsp;
+export function <span class="fstat-no" title="function not covered" >getAuthHeaders(</span>token: string) {
+  const auth = <span class="cstat-no" title="statement not covered" >'UpToken ' + token</span>
+<span class="cstat-no" title="statement not covered" >  return { Authorization: auth }</span>
+}
+&nbsp;
+export function <span class="fstat-no" title="function not covered" >getHeadersForChunkUpload(</span>token: string) {
+  const header = <span class="cstat-no" title="statement not covered" >getAuthHeaders(token)</span>
+<span class="cstat-no" title="statement not covered" >  return {</span>
+    'content-type': 'application/octet-stream',
+    ...header
+  }
+}
+&nbsp;
+export function <span class="fstat-no" title="function not covered" >getHeadersForMkFile(</span>token: string) {
+  const header = <span class="cstat-no" title="statement not covered" >getAuthHeaders(token)</span>
+<span class="cstat-no" title="statement not covered" >  return {</span>
+    'content-type': 'application/json',
+    ...header
+  }
+}
+&nbsp;
+export function <span class="fstat-no" title="function not covered" >createXHR(</span>): XMLHttpRequest {
+<span class="cstat-no" title="statement not covered" >  if (window.XMLHttpRequest) {</span>
+<span class="cstat-no" title="statement not covered" >    return new XMLHttpRequest()</span>
+  }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >  return window.ActiveXObject('Microsoft.XMLHTTP')</span>
+}
+&nbsp;
+export async function <span class="fstat-no" title="function not covered" >computeMd5(</span>data: Blob): Promise&lt;string&gt; {
+  const <span class="cstat-no" title="statement not covered" >buffer = <span class="cstat-no" title="statement not covered" ><span class="branch-0 cbranch-no" title="branch not covered" >await readAsArrayBuffer(data)</span></span></span>
+  const <span class="cstat-no" title="statement not covered" >spark = new SparkMD5.ArrayBuffer()</span>
+<span class="cstat-no" title="statement not covered" >  spark.append(buffer)</span>
+<span class="cstat-no" title="statement not covered" >  return spark.end()</span>
+}
+&nbsp;
+export function <span class="fstat-no" title="function not covered" >readAsArrayBuffer(</span>data: Blob): Promise&lt;ArrayBuffer&gt; {
+<span class="cstat-no" title="statement not covered" >  return new Promise(<span class="fstat-no" title="function not covered" >(r</span>esolve, reject) =&gt; {</span>
+    const reader = <span class="cstat-no" title="statement not covered" >new FileReader()</span>
+    // evt 类型目前存在问题 https://github.com/Microsoft/TypeScript/issues/4163
+<span class="cstat-no" title="statement not covered" >    reader.onload = <span class="fstat-no" title="function not covered" >(e</span>vt: ProgressEvent&lt;FileReader&gt;) =&gt; {</span>
+<span class="cstat-no" title="statement not covered" >      if (evt.target) {</span>
+        const body = <span class="cstat-no" title="statement not covered" >evt.target.result</span>
+<span class="cstat-no" title="statement not covered" >        resolve(body as ArrayBuffer)</span>
+      } else {
+<span class="cstat-no" title="statement not covered" >        reject(new Error('progress event target is undefined'))</span>
+      }
+    }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    reader.onerror = <span class="fstat-no" title="function not covered" >() =&gt; {</span></span>
+<span class="cstat-no" title="statement not covered" >      reject(new Error('fileReader read failed'))</span>
+    }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    reader.readAsArrayBuffer(data)</span>
+  })
+}
+&nbsp;
+export interface ResponseSuccess&lt;T&gt; {
+  data: T
+  reqId: string
+}
+&nbsp;
+export interface ResponseError {
+  code: number /** 请求错误状态码,只有在 err.isRequestError 为 true 的时候才有效。可查阅码值对应说明。*/
+  message: string /** 错误信息,包含错误码,当后端返回提示信息时也会有相应的错误信息。 */
+  isRequestError: true | undefined /** 用于区分是否 xhr 请求错误当 xhr 请求出现错误并且后端通过 HTTP 状态码返回了错误信息时,该参数为 true否则为 undefined 。 */
+  reqId: string /** xhr请求错误的 X-Reqid。 */
+}
+&nbsp;
+export type CustomError = ResponseError | Error | any
+&nbsp;
+export type XHRHandler = (xhr: XMLHttpRequest) =&gt; void
+&nbsp;
+export interface RequestOptions {
+  method: string
+  onProgress?: (data: Progress) =&gt; void
+  onCreate?: XHRHandler
+  body?: BodyInit | null
+  headers?: { [key: string]: string }
+}
+&nbsp;
+export type Response&lt;T&gt; = Promise&lt;ResponseSuccess&lt;T&gt;&gt;
+&nbsp;
+export function <span class="fstat-no" title="function not covered" >request&lt;</span>T&gt;(url: string, options: RequestOptions): Response&lt;T&gt; {
+<span class="cstat-no" title="statement not covered" >  return new Promise(<span class="fstat-no" title="function not covered" >(r</span>esolve, reject) =&gt; {</span>
+    const xhr = <span class="cstat-no" title="statement not covered" >createXHR()</span>
+<span class="cstat-no" title="statement not covered" >    xhr.open(options.method, url)</span>
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    if (options.onCreate) {</span>
+<span class="cstat-no" title="statement not covered" >      options.onCreate(xhr)</span>
+    }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    if (options.headers) {</span>
+      const headers = <span class="cstat-no" title="statement not covered" >options.headers</span>
+<span class="cstat-no" title="statement not covered" >      Object.keys(headers).forEach(<span class="fstat-no" title="function not covered" >k </span>=&gt; {</span>
+<span class="cstat-no" title="statement not covered" >        xhr.setRequestHeader(k, headers[k])</span>
+      })
+    }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    xhr.upload.addEventListener('progress', <span class="fstat-no" title="function not covered" >(e</span>vt: ProgressEvent) =&gt; {</span>
+<span class="cstat-no" title="statement not covered" >      if (evt.lengthComputable &amp;&amp; options.onProgress) {</span>
+<span class="cstat-no" title="statement not covered" >        options.onProgress({</span>
+          loaded: evt.loaded,
+          total: evt.total
+        })
+      }
+    })
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    xhr.onreadystatechange = <span class="fstat-no" title="function not covered" >() =&gt; {</span></span>
+      const responseText = <span class="cstat-no" title="statement not covered" >xhr.responseText</span>
+<span class="cstat-no" title="statement not covered" >      if (xhr.readyState !== 4) {</span>
+<span class="cstat-no" title="statement not covered" >        return</span>
+      }
+&nbsp;
+      const reqId = <span class="cstat-no" title="statement not covered" >xhr.getResponseHeader('x-reqId') || ''</span>
+<span class="cstat-no" title="statement not covered" >      if (xhr.status !== 200) {</span>
+        let message = <span class="cstat-no" title="statement not covered" >`xhr request failed, code: ${xhr.status}`</span>
+<span class="cstat-no" title="statement not covered" >        if (responseText) {</span>
+<span class="cstat-no" title="statement not covered" >          message += ` response: ${responseText}`</span>
+        }
+<span class="cstat-no" title="statement not covered" >        reject({</span>
+          code: xhr.status,
+          message,
+          reqId,
+          isRequestError: true
+        })
+<span class="cstat-no" title="statement not covered" >        return</span>
+      }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >      try {</span>
+<span class="cstat-no" title="statement not covered" >        resolve({</span>
+          data: JSON.parse(responseText),
+          reqId
+        })
+      } catch (err) {
+<span class="cstat-no" title="statement not covered" >        reject(err)</span>
+      }
+    }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    xhr.send(options.body)</span>
+  })
+}
+&nbsp;
+export function <span class="fstat-no" title="function not covered" >getPortFromUrl(</span>url: string) {
+<span class="cstat-no" title="statement not covered" >  if (url &amp;&amp; url.match) {</span>
+    let groups = <span class="cstat-no" title="statement not covered" >url.match(/(^https?)/)</span>
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    if (!groups) {</span>
+<span class="cstat-no" title="statement not covered" >      return ''</span>
+    }
+&nbsp;
+    const type = <span class="cstat-no" title="statement not covered" >groups[1]</span>
+<span class="cstat-no" title="statement not covered" >    groups = url.match(/^https?:\/\/([^:^/]*):(\d*)/)</span>
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    if (groups) {</span>
+<span class="cstat-no" title="statement not covered" >      return groups[2]</span>
+    }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    if (type === 'http') {</span>
+<span class="cstat-no" title="statement not covered" >      return '80'</span>
+    }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    return '443'</span>
+  }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >  return ''</span>
+}
+&nbsp;
+export function <span class="fstat-no" title="function not covered" >getDomainFromUrl(</span>url: string): string {
+<span class="cstat-no" title="statement not covered" >  if (url &amp;&amp; url.match) {</span>
+    const groups = <span class="cstat-no" title="statement not covered" >url.match(/^https?:\/\/([^:^/]*)/)</span>
+<span class="cstat-no" title="statement not covered" >    return groups ? groups[1] : ''</span>
+  }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >  return ''</span>
+}
+&nbsp;
+interface PutPolicy {
+  ak: string
+  scope: string
+}
+&nbsp;
+export function <span class="fstat-no" title="function not covered" >getPutPolicy(</span>token: string) {
+  const segments = <span class="cstat-no" title="statement not covered" >token.split(':')</span>
+  // token 构造的差异参考:https://github.com/qbox/product/blob/master/kodo/auths/UpToken.md#admin-uptoken-authorization
+  const ak = <span class="cstat-no" title="statement not covered" >segments.length &gt; 3 ? segments[1] : segments[0]</span>
+  const putPolicy: PutPolicy = <span class="cstat-no" title="statement not covered" >JSON.parse(urlSafeBase64Decode(segments[segments.length - 1]))</span>
+&nbsp;
+<span class="cstat-no" title="statement not covered" >  return {</span>
+    ak,
+    bucket: putPolicy.scope.split(':')[0]
+  }
+}
+&nbsp;
+export function <span class="fstat-no" title="function not covered" >createObjectURL(</span>file: File) {
+  const URL = <span class="cstat-no" title="statement not covered" >window.URL || window.webkitURL || window.mozURL</span>
+<span class="cstat-no" title="statement not covered" >  return URL.createObjectURL(file)</span>
+}
+&nbsp;
+export interface TransformValue {
+  width: number
+  height: number
+  matrix: [number, number, number, number, number, number]
+}
+&nbsp;
+export function <span class="fstat-no" title="function not covered" >getTransform(</span>image: HTMLImageElement, orientation: number): TransformValue {
+  const { width, height } = <span class="cstat-no" title="statement not covered" >image</span>
+&nbsp;
+<span class="cstat-no" title="statement not covered" >  switch (orientation) {</span>
+    case 1:
+      // default
+<span class="cstat-no" title="statement not covered" >      return {</span>
+        width,
+        height,
+        matrix: [1, 0, 0, 1, 0, 0]
+      }
+    case 2:
+      // horizontal flip
+<span class="cstat-no" title="statement not covered" >      return {</span>
+        width,
+        height,
+        matrix: [-1, 0, 0, 1, width, 0]
+      }
+    case 3:
+      // 180° rotated
+<span class="cstat-no" title="statement not covered" >      return {</span>
+        width,
+        height,
+        matrix: [-1, 0, 0, -1, width, height]
+      }
+    case 4:
+      // vertical flip
+<span class="cstat-no" title="statement not covered" >      return {</span>
+        width,
+        height,
+        matrix: [1, 0, 0, -1, 0, height]
+      }
+    case 5:
+      // vertical flip + -90° rotated
+<span class="cstat-no" title="statement not covered" >      return {</span>
+        width: height,
+        height: width,
+        matrix: [0, 1, 1, 0, 0, 0]
+      }
+    case 6:
+      // -90° rotated
+<span class="cstat-no" title="statement not covered" >      return {</span>
+        width: height,
+        height: width,
+        matrix: [0, 1, -1, 0, height, 0]
+      }
+    case 7:
+      // horizontal flip + -90° rotate
+<span class="cstat-no" title="statement not covered" >      return {</span>
+        width: height,
+        height: width,
+        matrix: [0, -1, -1, 0, height, width]
+      }
+    case 8:
+      // 90° rotated
+<span class="cstat-no" title="statement not covered" >      return {</span>
+        width: height,
+        height: width,
+        matrix: [0, -1, 1, 0, 0, width]
+      }
+    default:
+<span class="cstat-no" title="statement not covered" >      throw new Error(`orientation ${orientation} is unavailable`)</span>
+  }
+}
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Thu Apr 22 2021 01:38:54 GMT+0000 (Coordinated Universal Time)
+            </div>
+        </div>
+        <script src="../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../sorter.js"></script>
+        <script src="../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 1000 - 0
qiniu-js/coverage/lcov.info

@@ -0,0 +1,1000 @@
+TN:
+SF:src/api.ts
+FN:16,getUpHosts
+FN:25,getUploadUrl
+FN:48,getBaseUrl
+FN:66,initUploadParts
+FN:93,uploadChunk
+FN:117,uploadComplete
+FN:137,deleteUploadedChunks
+FNF:7
+FNH:2
+FNDA:1,getUpHosts
+FNDA:5,getUploadUrl
+FNDA:0,getBaseUrl
+FNDA:0,initUploadParts
+FNDA:0,uploadChunk
+FNDA:0,uploadComplete
+FNDA:0,deleteUploadedChunks
+DA:1,1
+DA:2,1
+DA:3,1
+DA:16,1
+DA:17,1
+DA:18,1
+DA:19,1
+DA:25,1
+DA:26,5
+DA:28,5
+DA:29,1
+DA:32,4
+DA:33,3
+DA:34,3
+DA:35,3
+DA:38,1
+DA:39,1
+DA:40,1
+DA:49,0
+DA:50,0
+DA:66,1
+DA:72,0
+DA:73,0
+DA:93,1
+DA:100,0
+DA:101,0
+DA:102,0
+DA:117,1
+DA:123,0
+DA:124,0
+DA:125,0
+DA:137,1
+DA:142,0
+DA:143,0
+DA:144,0
+LF:35
+LH:22
+BRDA:26,0,0,5
+BRDA:26,0,1,0
+BRDA:28,1,0,1
+BRDA:28,1,1,4
+BRDA:32,2,0,3
+BRDA:32,2,1,1
+BRDA:34,3,0,3
+BRDA:34,3,1,0
+BRDA:50,4,0,0
+BRDA:50,4,1,0
+BRDA:72,5,0,0
+BRDA:72,5,1,0
+BRF:12
+BRH:6
+end_of_record
+TN:
+SF:src/base64.ts
+FN:3,utf8Encode
+FN:78,base64Encode
+FN:146,base64Decode
+FN:202,urlSafeBase64Encode
+FN:207,urlSafeBase64Decode
+FNF:5
+FNH:3
+FNDA:2,utf8Encode
+FNDA:2,base64Encode
+FNDA:0,base64Decode
+FNDA:2,urlSafeBase64Encode
+FNDA:0,urlSafeBase64Decode
+DA:3,3
+DA:19,2
+DA:20,0
+DA:23,2
+DA:24,2
+DA:27,2
+DA:29,2
+DA:30,2
+DA:31,2
+DA:32,86
+DA:33,86
+DA:35,86
+DA:36,86
+DA:37,0
+DA:38,0
+DA:39,0
+DA:40,0
+DA:47,0
+DA:48,0
+DA:50,0
+DA:51,0
+DA:52,0
+DA:54,0
+DA:55,0
+DA:62,86
+DA:63,0
+DA:64,0
+DA:66,0
+DA:67,0
+DA:71,2
+DA:72,2
+DA:75,2
+DA:78,3
+DA:94,2
+DA:103,2
+DA:104,2
+DA:105,2
+DA:106,2
+DA:108,2
+DA:109,0
+DA:112,2
+DA:114,2
+DA:116,30
+DA:117,30
+DA:118,30
+DA:120,30
+DA:122,30
+DA:123,30
+DA:124,30
+DA:125,30
+DA:128,30
+DA:132,2
+DA:134,2
+DA:136,2
+DA:137,2
+DA:139,0
+DA:140,0
+DA:143,2
+DA:146,3
+DA:164,0
+DA:165,0
+DA:166,0
+DA:167,0
+DA:168,0
+DA:170,0
+DA:171,0
+DA:174,0
+DA:176,0
+DA:177,0
+DA:178,0
+DA:179,0
+DA:180,0
+DA:182,0
+DA:184,0
+DA:185,0
+DA:186,0
+DA:188,0
+DA:189,0
+DA:190,0
+DA:191,0
+DA:193,0
+DA:197,0
+DA:199,0
+DA:202,3
+DA:203,2
+DA:204,2
+DA:207,3
+DA:208,0
+DA:209,0
+LF:89
+LH:44
+BRDA:19,0,0,0
+BRDA:19,0,1,2
+BRDA:19,1,0,2
+BRDA:19,1,1,2
+BRDA:35,2,0,86
+BRDA:35,2,1,0
+BRDA:37,3,0,0
+BRDA:37,3,1,0
+BRDA:37,4,0,0
+BRDA:37,4,1,0
+BRDA:39,5,0,0
+BRDA:39,5,1,0
+BRDA:47,6,0,0
+BRDA:47,6,1,0
+BRDA:51,7,0,0
+BRDA:51,7,1,0
+BRDA:62,8,0,0
+BRDA:62,8,1,86
+BRDA:63,9,0,0
+BRDA:63,9,1,0
+BRDA:71,10,0,2
+BRDA:71,10,1,0
+BRDA:108,11,0,0
+BRDA:108,11,1,2
+BRDA:135,12,0,2
+BRDA:135,12,1,0
+BRDA:170,13,0,0
+BRDA:170,13,1,0
+BRDA:188,14,0,0
+BRDA:188,14,1,0
+BRDA:190,15,0,0
+BRDA:190,15,1,0
+BRF:32
+BRH:8
+end_of_record
+TN:
+SF:src/config.ts
+FNF:0
+FNH:0
+DA:2,1
+DA:11,1
+LF:2
+LH:2
+BRF:0
+BRH:0
+end_of_record
+TN:
+SF:src/image.ts
+FN:47,getImageUrl
+FN:56,imageView2
+FN:79,imageMogr2
+FN:101,watermark
+FN:144,imageInfo
+FN:150,exif
+FN:155,pipeline
+FNF:7
+FNH:4
+FNDA:3,getImageUrl
+FNDA:1,imageView2
+FNDA:1,imageMogr2
+FNDA:2,watermark
+FNDA:0,imageInfo
+FNDA:0,exif
+FNDA:0,pipeline
+DA:1,1
+DA:2,1
+DA:48,3
+DA:49,3
+DA:50,3
+DA:53,3
+DA:56,1
+DA:57,1
+DA:58,0
+DA:61,5
+DA:63,1
+DA:64,0
+DA:67,1
+DA:68,1
+DA:69,1
+DA:70,1
+DA:71,1
+DA:72,1
+DA:73,1
+DA:75,1
+DA:79,1
+DA:80,1
+DA:81,8
+DA:83,1
+DA:85,1
+DA:86,1
+DA:87,1
+DA:88,1
+DA:89,1
+DA:90,1
+DA:91,1
+DA:92,1
+DA:93,1
+DA:94,1
+DA:95,1
+DA:97,1
+DA:101,1
+DA:102,2
+DA:103,2
+DA:104,0
+DA:107,2
+DA:108,2
+DA:109,1
+DA:112,1
+DA:113,1
+DA:114,1
+DA:115,0
+DA:117,1
+DA:120,1
+DA:121,0
+DA:122,0
+DA:123,0
+DA:125,0
+DA:126,0
+DA:127,0
+DA:128,0
+DA:131,4
+DA:133,1
+DA:134,1
+DA:135,1
+DA:136,1
+DA:137,1
+DA:138,1
+DA:140,1
+DA:144,1
+DA:145,0
+DA:146,0
+DA:150,1
+DA:151,0
+DA:152,0
+DA:155,1
+DA:156,0
+DA:158,0
+DA:159,0
+DA:160,0
+DA:161,0
+DA:162,0
+DA:163,0
+DA:164,0
+DA:166,0
+DA:168,0
+DA:169,0
+DA:171,0
+DA:172,0
+DA:174,0
+DA:175,0
+DA:177,0
+DA:178,0
+DA:180,0
+DA:181,0
+DA:185,0
+DA:186,0
+DA:187,0
+DA:188,0
+DA:189,0
+DA:192,0
+DA:195,0
+LF:97
+LH:56
+BRDA:49,0,0,3
+BRDA:49,0,1,0
+BRDA:57,1,0,0
+BRDA:57,1,1,1
+BRDA:63,2,0,0
+BRDA:63,2,1,1
+BRDA:63,3,0,1
+BRDA:63,3,1,1
+BRDA:68,4,0,0
+BRDA:68,4,1,1
+BRDA:69,5,0,1
+BRDA:69,5,1,0
+BRDA:70,6,0,1
+BRDA:70,6,1,0
+BRDA:71,7,0,0
+BRDA:71,7,1,1
+BRDA:72,8,0,1
+BRDA:72,8,1,0
+BRDA:72,9,0,1
+BRDA:72,9,1,1
+BRDA:85,10,0,0
+BRDA:85,10,1,1
+BRDA:86,11,0,1
+BRDA:86,11,1,0
+BRDA:87,12,0,1
+BRDA:87,12,1,0
+BRDA:88,13,0,1
+BRDA:88,13,1,0
+BRDA:89,14,0,1
+BRDA:89,14,1,0
+BRDA:90,15,0,1
+BRDA:90,15,1,0
+BRDA:91,16,0,1
+BRDA:91,16,1,0
+BRDA:92,17,0,1
+BRDA:92,17,1,0
+BRDA:93,18,0,1
+BRDA:93,18,1,0
+BRDA:94,19,0,1
+BRDA:94,19,1,0
+BRDA:94,20,0,1
+BRDA:94,20,1,1
+BRDA:103,21,0,0
+BRDA:103,21,1,2
+BRDA:108,22,0,1
+BRDA:108,22,1,1
+BRDA:108,23,0,2
+BRDA:108,23,1,1
+BRDA:112,24,0,1
+BRDA:112,24,1,0
+BRDA:114,25,0,0
+BRDA:114,25,1,1
+BRDA:117,26,0,1
+BRDA:117,26,1,0
+BRDA:120,27,0,0
+BRDA:120,27,1,1
+BRDA:122,28,0,0
+BRDA:122,28,1,0
+BRDA:125,29,0,0
+BRDA:125,29,1,0
+BRDA:126,30,0,0
+BRDA:126,30,1,0
+BRDA:127,31,0,0
+BRDA:127,31,1,0
+BRDA:128,32,0,0
+BRDA:128,32,1,0
+BRDA:133,33,0,1
+BRDA:133,33,1,0
+BRDA:134,34,0,0
+BRDA:134,34,1,1
+BRDA:135,35,0,1
+BRDA:135,35,1,0
+BRDA:136,36,0,1
+BRDA:136,36,1,0
+BRDA:137,37,0,1
+BRDA:137,37,1,0
+BRDA:137,38,0,1
+BRDA:137,38,1,1
+BRDA:160,39,0,0
+BRDA:160,39,1,0
+BRDA:163,40,0,0
+BRDA:163,40,1,0
+BRDA:167,41,0,0
+BRDA:167,41,1,0
+BRDA:167,41,2,0
+BRDA:167,41,3,0
+BRDA:180,42,0,0
+BRDA:180,42,1,0
+BRDA:185,43,0,0
+BRDA:185,43,1,0
+BRDA:185,44,0,0
+BRDA:185,44,1,0
+BRDA:188,45,0,0
+BRDA:188,45,1,0
+BRF:94
+BRH:40
+end_of_record
+TN:
+SF:src/pool.ts
+FN:9,(anonymous_0)
+FN:13,Pool
+FN:15,(anonymous_2)
+FN:16,(anonymous_3)
+FN:26,(anonymous_4)
+FN:27,(anonymous_5)
+FN:30,(anonymous_6)
+FN:31,(anonymous_7)
+FN:35,(anonymous_8)
+FN:39,(anonymous_9)
+FN:42,(anonymous_10)
+FNF:11
+FNH:10
+FNDA:1,(anonymous_0)
+FNDA:1,Pool
+FNDA:6,(anonymous_2)
+FNDA:6,(anonymous_3)
+FNDA:6,(anonymous_4)
+FNDA:12,(anonymous_5)
+FNDA:6,(anonymous_6)
+FNDA:11,(anonymous_7)
+FNDA:0,(anonymous_8)
+FNDA:12,(anonymous_9)
+FNDA:6,(anonymous_10)
+DA:9,1
+DA:10,1
+DA:11,1
+DA:13,1
+DA:15,6
+DA:16,6
+DA:17,6
+DA:22,6
+DA:26,6
+DA:27,12
+DA:28,6
+DA:29,6
+DA:31,11
+DA:32,6
+DA:33,6
+DA:35,0
+DA:39,12
+DA:40,12
+DA:41,12
+DA:42,12
+DA:43,6
+DA:46,1
+LF:22
+LH:21
+BRF:0
+BRH:0
+end_of_record
+TN:
+SF:src/utils.ts
+FN:8,getChunks
+FN:33,isMetaDataValid
+FN:34,(anonymous_18)
+FN:37,isCustomVarsValid
+FN:38,(anonymous_20)
+FN:41,sum
+FN:42,(anonymous_22)
+FN:45,setLocalFileInfo
+FN:53,createLocalKey
+FN:58,removeLocalFileInfo
+FN:66,getLocalFileInfo
+FN:86,getAuthHeaders
+FN:91,getHeadersForChunkUpload
+FN:99,getHeadersForMkFile
+FN:107,createXHR
+FN:115,computeMd5
+FN:122,readAsArrayBuffer
+FN:123,(anonymous_35)
+FN:126,(anonymous_36)
+FN:135,(anonymous_37)
+FN:169,request
+FN:170,(anonymous_39)
+FN:180,(anonymous_40)
+FN:185,(anonymous_41)
+FN:194,(anonymous_42)
+FN:229,getPortFromUrl
+FN:254,getDomainFromUrl
+FN:268,getPutPolicy
+FN:280,createObjectURL
+FN:291,getTransform
+FNF:30
+FNH:1
+FNDA:0,getChunks
+FNDA:0,isMetaDataValid
+FNDA:0,(anonymous_18)
+FNDA:0,isCustomVarsValid
+FNDA:0,(anonymous_20)
+FNDA:0,sum
+FNDA:0,(anonymous_22)
+FNDA:0,setLocalFileInfo
+FNDA:2,createLocalKey
+FNDA:0,removeLocalFileInfo
+FNDA:0,getLocalFileInfo
+FNDA:0,getAuthHeaders
+FNDA:0,getHeadersForChunkUpload
+FNDA:0,getHeadersForMkFile
+FNDA:0,createXHR
+FNDA:0,computeMd5
+FNDA:0,readAsArrayBuffer
+FNDA:0,(anonymous_35)
+FNDA:0,(anonymous_36)
+FNDA:0,(anonymous_37)
+FNDA:0,request
+FNDA:0,(anonymous_39)
+FNDA:0,(anonymous_40)
+FNDA:0,(anonymous_41)
+FNDA:0,(anonymous_42)
+FNDA:0,getPortFromUrl
+FNDA:0,getDomainFromUrl
+FNDA:0,getPutPolicy
+FNDA:0,createObjectURL
+FNDA:0,getTransform
+DA:1,2
+DA:3,2
+DA:5,2
+DA:8,2
+DA:10,0
+DA:12,0
+DA:13,0
+DA:16,0
+DA:17,0
+DA:21,0
+DA:22,0
+DA:23,0
+DA:24,0
+DA:28,0
+DA:30,0
+DA:33,2
+DA:34,0
+DA:37,2
+DA:38,0
+DA:41,2
+DA:42,0
+DA:45,2
+DA:46,0
+DA:47,0
+DA:49,0
+DA:53,2
+DA:54,2
+DA:55,2
+DA:58,2
+DA:59,0
+DA:60,0
+DA:62,0
+DA:66,2
+DA:67,0
+DA:68,0
+DA:69,0
+DA:71,0
+DA:72,0
+DA:75,0
+DA:76,0
+DA:79,0
+DA:80,0
+DA:83,0
+DA:86,2
+DA:87,0
+DA:88,0
+DA:91,2
+DA:92,0
+DA:93,0
+DA:99,2
+DA:100,0
+DA:101,0
+DA:107,2
+DA:108,0
+DA:109,0
+DA:112,0
+DA:115,2
+DA:116,0
+DA:117,0
+DA:118,0
+DA:119,0
+DA:122,2
+DA:123,0
+DA:124,0
+DA:126,0
+DA:127,0
+DA:128,0
+DA:129,0
+DA:131,0
+DA:135,0
+DA:136,0
+DA:139,0
+DA:169,2
+DA:170,0
+DA:171,0
+DA:172,0
+DA:174,0
+DA:175,0
+DA:178,0
+DA:179,0
+DA:180,0
+DA:181,0
+DA:185,0
+DA:186,0
+DA:187,0
+DA:194,0
+DA:195,0
+DA:196,0
+DA:197,0
+DA:200,0
+DA:201,0
+DA:202,0
+DA:203,0
+DA:204,0
+DA:206,0
+DA:212,0
+DA:215,0
+DA:216,0
+DA:221,0
+DA:225,0
+DA:229,2
+DA:230,0
+DA:231,0
+DA:233,0
+DA:234,0
+DA:237,0
+DA:238,0
+DA:240,0
+DA:241,0
+DA:244,0
+DA:245,0
+DA:248,0
+DA:251,0
+DA:254,2
+DA:255,0
+DA:256,0
+DA:257,0
+DA:260,0
+DA:268,2
+DA:269,0
+DA:271,0
+DA:272,0
+DA:274,0
+DA:280,2
+DA:281,0
+DA:282,0
+DA:291,2
+DA:292,0
+DA:294,0
+DA:297,0
+DA:304,0
+DA:311,0
+DA:318,0
+DA:325,0
+DA:332,0
+DA:339,0
+DA:346,0
+DA:352,0
+LF:138
+LH:25
+BRDA:12,0,0,0
+BRDA:12,0,1,0
+BRDA:26,1,0,0
+BRDA:26,1,1,0
+BRDA:54,2,0,1
+BRDA:54,2,1,1
+BRDA:71,3,0,0
+BRDA:71,3,1,0
+BRDA:108,4,0,0
+BRDA:108,4,1,0
+BRDA:116,5,0,0
+BRDA:127,6,0,0
+BRDA:127,6,1,0
+BRDA:174,7,0,0
+BRDA:174,7,1,0
+BRDA:178,8,0,0
+BRDA:178,8,1,0
+BRDA:186,9,0,0
+BRDA:186,9,1,0
+BRDA:186,10,0,0
+BRDA:186,10,1,0
+BRDA:196,11,0,0
+BRDA:196,11,1,0
+BRDA:200,12,0,0
+BRDA:200,12,1,0
+BRDA:201,13,0,0
+BRDA:201,13,1,0
+BRDA:203,14,0,0
+BRDA:203,14,1,0
+BRDA:230,15,0,0
+BRDA:230,15,1,0
+BRDA:230,16,0,0
+BRDA:230,16,1,0
+BRDA:233,17,0,0
+BRDA:233,17,1,0
+BRDA:240,18,0,0
+BRDA:240,18,1,0
+BRDA:244,19,0,0
+BRDA:244,19,1,0
+BRDA:255,20,0,0
+BRDA:255,20,1,0
+BRDA:255,21,0,0
+BRDA:255,21,1,0
+BRDA:257,22,0,0
+BRDA:257,22,1,0
+BRDA:271,23,0,0
+BRDA:271,23,1,0
+BRDA:281,24,0,0
+BRDA:281,24,1,0
+BRDA:281,24,2,0
+BRDA:295,25,0,0
+BRDA:295,25,1,0
+BRDA:295,25,2,0
+BRDA:295,25,3,0
+BRDA:295,25,4,0
+BRDA:295,25,5,0
+BRDA:295,25,6,0
+BRDA:295,25,7,0
+BRDA:295,25,8,0
+BRF:59
+BRH:2
+end_of_record
+TN:
+SF:src/logger/index.ts
+FN:5,(anonymous_2)
+FN:12,Logger
+FN:23,(anonymous_4)
+FN:33,(anonymous_5)
+FN:44,(anonymous_6)
+FN:55,(anonymous_7)
+FNF:6
+FNH:6
+FNDA:1,(anonymous_2)
+FNDA:9,Logger
+FNDA:2,(anonymous_4)
+FNDA:4,(anonymous_5)
+FNDA:4,(anonymous_6)
+FNDA:4,(anonymous_7)
+DA:1,1
+DA:5,1
+DA:6,1
+DA:10,9
+DA:13,9
+DA:14,9
+DA:15,9
+DA:23,1
+DA:24,2
+DA:25,1
+DA:26,0
+DA:33,12
+DA:34,4
+DA:35,4
+DA:36,1
+DA:44,12
+DA:45,4
+DA:46,4
+DA:47,2
+DA:55,12
+DA:56,4
+DA:57,4
+DA:58,3
+DA:61,1
+LF:24
+LH:23
+BRDA:14,0,0,0
+BRDA:14,0,1,9
+BRDA:15,1,0,0
+BRDA:15,1,1,9
+BRDA:24,2,0,1
+BRDA:24,2,1,1
+BRDA:35,3,0,1
+BRDA:35,3,1,3
+BRDA:46,4,0,2
+BRDA:46,4,1,2
+BRDA:57,5,0,3
+BRDA:57,5,1,1
+BRF:12
+BRH:10
+end_of_record
+TN:
+SF:src/logger/report-v3.ts
+FN:22,reportV3
+FN:27,(anonymous_1)
+FNF:2
+FNH:2
+FNDA:11,reportV3
+FNDA:29,(anonymous_1)
+DA:1,1
+DA:22,12
+DA:23,11
+DA:24,11
+DA:25,11
+DA:26,11
+DA:27,11
+DA:28,29
+DA:29,7
+DA:34,11
+DA:47,11
+LF:11
+LH:11
+BRDA:22,0,0,1
+BRDA:22,0,1,10
+BRDA:28,1,0,7
+BRDA:28,1,1,22
+BRDA:28,2,0,29
+BRDA:28,2,1,28
+BRDA:28,2,2,28
+BRDA:35,3,0,11
+BRDA:35,3,1,0
+BRDA:36,4,0,11
+BRDA:36,4,1,0
+BRDA:37,5,0,11
+BRDA:37,5,1,0
+BRDA:38,6,0,11
+BRDA:38,6,1,0
+BRDA:39,7,0,11
+BRDA:39,7,1,0
+BRDA:40,8,0,11
+BRDA:40,8,1,0
+BRDA:41,9,0,11
+BRDA:41,9,1,0
+BRDA:42,10,0,11
+BRDA:42,10,1,0
+BRDA:43,11,0,11
+BRDA:43,11,1,0
+BRDA:44,12,0,11
+BRDA:44,12,1,0
+BRF:27
+BRH:17
+end_of_record
+TN:
+SF:src/upload/base.ts
+FN:87,(anonymous_21)
+FN:107,Base
+FN:146,(anonymous_23)
+FN:156,(anonymous_24)
+FN:216,(anonymous_27)
+FN:218,(anonymous_28)
+FN:226,(anonymous_29)
+FN:232,(anonymous_30)
+FN:236,(anonymous_31)
+FN:251,(anonymous_32)
+FNF:10
+FNH:1
+FNDA:1,(anonymous_21)
+FNDA:0,Base
+FNDA:0,(anonymous_23)
+FNDA:0,(anonymous_24)
+FNDA:0,(anonymous_27)
+FNDA:0,(anonymous_28)
+FNDA:0,(anonymous_29)
+FNDA:0,(anonymous_30)
+FNDA:0,(anonymous_31)
+FNDA:0,(anonymous_32)
+DA:2,1
+DA:4,1
+DA:6,1
+DA:85,1
+DA:87,1
+DA:90,0
+DA:93,0
+DA:94,0
+DA:107,0
+DA:108,0
+DA:121,0
+DA:123,0
+DA:128,0
+DA:130,0
+DA:131,0
+DA:132,0
+DA:134,0
+DA:135,0
+DA:136,0
+DA:138,0
+DA:139,0
+DA:141,0
+DA:142,0
+DA:146,1
+DA:147,0
+DA:148,0
+DA:149,0
+DA:156,1
+DA:157,0
+DA:158,0
+DA:159,0
+DA:160,0
+DA:163,0
+DA:164,0
+DA:165,0
+DA:168,0
+DA:169,0
+DA:170,0
+DA:171,0
+DA:175,0
+DA:176,0
+DA:177,0
+DA:178,0
+DA:183,0
+DA:184,0
+DA:185,0
+DA:187,0
+DA:188,0
+DA:189,0
+DA:190,0
+DA:192,0
+DA:194,0
+DA:195,0
+DA:196,0
+DA:197,0
+DA:198,0
+DA:201,0
+DA:202,0
+DA:206,0
+DA:207,0
+DA:208,0
+DA:209,0
+DA:212,0
+DA:216,1
+DA:217,0
+DA:218,0
+DA:219,0
+DA:220,0
+DA:222,0
+DA:223,0
+DA:226,1
+DA:227,0
+DA:228,0
+DA:229,0
+DA:232,1
+DA:233,0
+DA:236,1
+DA:237,0
+DA:251,1
+DA:252,0
+DA:258,1
+LF:81
+LH:13
+BRDA:158,0,0,0
+BRDA:158,0,1,0
+BRDA:163,1,0,0
+BRDA:163,1,1,0
+BRDA:168,2,0,0
+BRDA:168,2,1,0
+BRDA:169,3,0,0
+BRDA:169,3,1,0
+BRDA:175,4,0,0
+BRDA:175,4,1,0
+BRDA:176,5,0,0
+BRDA:176,5,1,0
+BRDA:195,6,0,0
+BRDA:195,6,1,0
+BRDA:196,7,0,0
+BRDA:196,7,1,0
+BRDA:197,8,0,0
+BRDA:197,8,1,0
+BRDA:201,9,0,0
+BRDA:201,9,1,0
+BRDA:201,9,2,0
+BRDA:206,10,0,0
+BRDA:206,10,1,0
+BRDA:206,11,0,0
+BRDA:206,11,1,0
+BRDA:206,11,2,0
+BRDA:245,12,0,0
+BRDA:245,12,1,0
+BRF:28
+BRH:0
+end_of_record

Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
qiniu-js/dist/qiniu.min.js


Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
qiniu-js/dist/qiniu.min.js.map


+ 1 - 0
qiniu-js/esm/__tests__/api.test.d.ts

@@ -0,0 +1 @@
+export {};

+ 102 - 0
qiniu-js/esm/__tests__/api.test.js

@@ -0,0 +1,102 @@
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+    function verb(n) { return function (v) { return step([n, v]); }; }
+    function step(op) {
+        if (f) throw new TypeError("Generator is already executing.");
+        while (_) try {
+            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+            if (y = 0, t) op = [op[0] & 2, t.value];
+            switch (op[0]) {
+                case 0: case 1: t = op; break;
+                case 4: _.label++; return { value: op[1], done: false };
+                case 5: _.label++; y = op[1]; op = [0]; continue;
+                case 7: op = _.ops.pop(); _.trys.pop(); continue;
+                default:
+                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+                    if (t[2]) _.ops.pop();
+                    _.trys.pop(); continue;
+            }
+            op = body.call(thisArg, _);
+        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+    }
+};
+import { getUploadUrl } from '../api';
+import { DEFAULT_CHUNK_SIZE } from '../upload/base';
+import { region } from '../config';
+jest.mock('../utils', function () { return ({
+    request: function () { return Promise.resolve({
+        data: {
+            up: {
+                acc: {
+                    main: ['mock.qiniu.com']
+                }
+            }
+        }
+    }); },
+    getPutPolicy: function () { return ({
+        ak: 'ak',
+        bucket: 'bucket'
+    }); }
+}); });
+describe('api function test', function () {
+    test('getUploadUrl', function () { return __awaiter(void 0, void 0, void 0, function () {
+        var config, url, token;
+        return __generator(this, function (_a) {
+            switch (_a.label) {
+                case 0:
+                    config = {
+                        useCdnDomain: true,
+                        disableStatisticsReport: false,
+                        retryCount: 3,
+                        checkByMD5: false,
+                        uphost: '',
+                        upprotocol: 'https:',
+                        forceDirect: false,
+                        chunkSize: DEFAULT_CHUNK_SIZE,
+                        concurrentRequestLimit: 3
+                    };
+                    token = 'token';
+                    return [4 /*yield*/, getUploadUrl(config, token)];
+                case 1:
+                    url = _a.sent();
+                    expect(url).toBe('https://mock.qiniu.com');
+                    config.region = region.z0;
+                    return [4 /*yield*/, getUploadUrl(config, token)];
+                case 2:
+                    url = _a.sent();
+                    expect(url).toBe('https://upload.qiniup.com');
+                    config.upprotocol = 'https:';
+                    return [4 /*yield*/, getUploadUrl(config, token)];
+                case 3:
+                    url = _a.sent();
+                    expect(url).toBe('https://upload.qiniup.com');
+                    config.upprotocol = 'http:';
+                    return [4 /*yield*/, getUploadUrl(config, token)];
+                case 4:
+                    url = _a.sent();
+                    expect(url).toBe('http://upload.qiniup.com');
+                    config.uphost = 'qiniu.com';
+                    return [4 /*yield*/, getUploadUrl(config, token)];
+                case 5:
+                    url = _a.sent();
+                    expect(url).toBe('http://qiniu.com');
+                    return [2 /*return*/];
+            }
+        });
+    }); });
+});
+//# sourceMappingURL=api.test.js.map

+ 1 - 0
qiniu-js/esm/__tests__/api.test.js.map

@@ -0,0 +1 @@
+{"version":3,"file":"api.test.js","sourceRoot":"","sources":["../../src/__tests__/api.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,EAAE,kBAAkB,EAAU,MAAM,gBAAgB,CAAA;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAElC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,cAAM,OAAA,CAAC;IAC3B,OAAO,EAAE,cAAM,OAAA,OAAO,CAAC,OAAO,CAAC;QAC7B,IAAI,EAAE;YACJ,EAAE,EAAE;gBACF,GAAG,EAAE;oBACH,IAAI,EAAE,CAAC,gBAAgB,CAAC;iBACzB;aACF;SACF;KACF,CAAC,EARa,CAQb;IACF,YAAY,EAAE,cAAM,OAAA,CAAC;QACnB,EAAE,EAAE,IAAI;QACR,MAAM,EAAE,QAAQ;KACjB,CAAC,EAHkB,CAGlB;CACH,CAAC,EAd0B,CAc1B,CAAC,CAAA;AAEH,QAAQ,CAAC,mBAAmB,EAAE;IAC5B,IAAI,CAAC,cAAc,EAAE;;;;;oBACf,MAAM,GAAW;wBACnB,YAAY,EAAE,IAAI;wBAClB,uBAAuB,EAAE,KAAK;wBAC9B,UAAU,EAAE,CAAC;wBACb,UAAU,EAAE,KAAK;wBACjB,MAAM,EAAE,EAAE;wBACV,UAAU,EAAE,QAAQ;wBACpB,WAAW,EAAE,KAAK;wBAClB,SAAS,EAAE,kBAAkB;wBAC7B,sBAAsB,EAAE,CAAC;qBAC1B,CAAA;oBAGK,KAAK,GAAG,OAAO,CAAA;oBAEf,qBAAM,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,EAAA;;oBAAvC,GAAG,GAAG,SAAiC,CAAA;oBACvC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAA;oBAE1C,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAA;oBACnB,qBAAM,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,EAAA;;oBAAvC,GAAG,GAAG,SAAiC,CAAA;oBACvC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAA;oBAE7C,MAAM,CAAC,UAAU,GAAG,QAAQ,CAAA;oBACtB,qBAAM,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,EAAA;;oBAAvC,GAAG,GAAG,SAAiC,CAAA;oBACvC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAA;oBAE7C,MAAM,CAAC,UAAU,GAAG,OAAO,CAAA;oBACrB,qBAAM,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,EAAA;;oBAAvC,GAAG,GAAG,SAAiC,CAAA;oBACvC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAA;oBAE5C,MAAM,CAAC,MAAM,GAAG,WAAW,CAAA;oBACrB,qBAAM,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,EAAA;;oBAAvC,GAAG,GAAG,SAAiC,CAAA;oBACvC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAA;;;;SACrC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}

+ 1 - 0
qiniu-js/esm/__tests__/image.test.d.ts

@@ -0,0 +1 @@
+export {};

+ 53 - 0
qiniu-js/esm/__tests__/image.test.js

@@ -0,0 +1,53 @@
+import { imageView2, imageMogr2, watermark } from '../image';
+import { urlSafeBase64Encode } from '../base64';
+describe('image func test', function () {
+    var domain = 'http://otxza7yo2.bkt.clouddn.com';
+    var key = 'test.png';
+    test('imageView2', function () {
+        var m = {
+            'fop': 'imageView2',
+            'mode': 2,
+            'h': 450,
+            'q': 100
+        };
+        var url = imageView2(m, key, domain);
+        expect(url).toBe('http://otxza7yo2.bkt.clouddn.com/' + key + '?' +
+            'imageView2/' + encodeURIComponent(m.mode) +
+            '/h' + '/' + encodeURIComponent(m.h) +
+            '/q' + '/' + encodeURIComponent(m.q));
+    });
+    test('imageMogr2', function () {
+        var m = {
+            thumbnail: 1,
+            strip: true,
+            gravity: 1,
+            crop: 1,
+            quality: 1,
+            rotate: 1,
+            format: 1,
+            blur: 1
+        };
+        var url = imageMogr2(m, key, domain);
+        expect(url).toBe('http://otxza7yo2.bkt.clouddn.com/' + key + '?imageMogr2/' +
+            'thumbnail/1/strip/gravity/1/quality/1/crop/1/rotate/1/format/1/blur/1');
+    });
+    test('watermark', function () {
+        var m = {
+            fop: 'watermark',
+            mode: 1,
+            image: 'http://www.b1.qiniudn.com/images/logo-2.png',
+            dissolve: 100,
+            dx: 100,
+            dy: 100
+        };
+        var url = watermark(m, key, domain);
+        expect(url).toBe('http://otxza7yo2.bkt.clouddn.com/' + key + '?' +
+            'watermark/' + m.mode + '/image/' + urlSafeBase64Encode(m.image) +
+            '/dissolve/100/dx/100/dy/100');
+        m.mode = 3;
+        expect(function () {
+            watermark(m, key, domain);
+        }).toThrow('mode is wrong');
+    });
+});
+//# sourceMappingURL=image.test.js.map

+ 1 - 0
qiniu-js/esm/__tests__/image.test.js.map

@@ -0,0 +1 @@
+{"version":3,"file":"image.test.js","sourceRoot":"","sources":["../../src/__tests__/image.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAa,MAAM,UAAU,CAAA;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAE/C,QAAQ,CAAC,iBAAiB,EAAE;IACxB,IAAM,MAAM,GAAG,kCAAkC,CAAA;IACjD,IAAM,GAAG,GAAG,UAAU,CAAA;IAExB,IAAI,CAAC,YAAY,EAAE;QACjB,IAAI,CAAC,GAAG;YACN,KAAK,EAAE,YAAY;YACnB,MAAM,EAAE,CAAC;YACT,GAAG,EAAE,GAAG;YACR,GAAG,EAAE,GAAG;SACT,CAAA;QACD,IAAI,GAAG,GAAG,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,CAAA;QACpC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CACd,mCAAmC,GAAG,GAAG,GAAG,GAAG;YAC/C,aAAa,GAAG,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC;YAC1C,IAAI,GAAG,GAAG,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;YACpC,IAAI,GAAG,GAAG,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CACrC,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,YAAY,EAAE;QACjB,IAAI,CAAC,GAAG;YACN,SAAS,EAAE,CAAC;YACZ,KAAK,EAAE,IAAI;YACX,OAAO,EAAE,CAAC;YACV,IAAI,EAAE,CAAC;YACP,OAAO,EAAE,CAAC;YACV,MAAM,EAAE,CAAC;YACT,MAAM,EAAE,CAAC;YACT,IAAI,EAAE,CAAC;SACR,CAAA;QAED,IAAI,GAAG,GAAG,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,CAAA;QACpC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CACd,mCAAmC,GAAG,GAAG,GAAG,cAAc;YAC1D,uEAAuE,CACxE,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,WAAW,EAAE;QAChB,IAAI,CAAC,GAAE;YACL,GAAG,EAAE,WAAW;YAChB,IAAI,EAAE,CAAC;YACP,KAAK,EAAE,6CAA6C;YACpD,QAAQ,EAAE,GAAG;YACb,EAAE,EAAE,GAAG;YACP,EAAE,EAAE,GAAG;SACR,CAAA;QACD,IAAI,GAAG,GAAG,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,CAAA;QACnC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CACd,mCAAmC,GAAG,GAAG,GAAG,GAAG;YAC/C,YAAY,GAAG,CAAC,CAAC,IAAI,GAAG,SAAS,GAAG,mBAAmB,CAAC,CAAC,CAAC,KAAK,CAAC;YAChE,6BAA6B,CAC9B,CAAA;QACD,CAAC,CAAC,IAAI,GAAG,CAAC,CAAA;QACV,MAAM,CAAC;YACL,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,CAAA;QAC3B,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,CAAA;IAC7B,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}

+ 1 - 0
qiniu-js/esm/__tests__/pool.test.d.ts

@@ -0,0 +1 @@
+export {};

+ 29 - 0
qiniu-js/esm/__tests__/pool.test.js

@@ -0,0 +1,29 @@
+import { Pool } from "../pool";
+var m = jest.fn();
+var task = function () {
+    return new Promise(function (resolve, _) {
+        m();
+        resolve();
+    });
+};
+describe("test Pool for control concurrency", function () {
+    var pool = new Pool(task, 2);
+    test("pool.js", function () {
+        var chunk = new Blob();
+        var data = [
+            { chunk: chunk, index: 0 },
+            { chunk: chunk, index: 1 },
+            { chunk: chunk, index: 2 },
+            { chunk: chunk, index: 3 },
+            { chunk: chunk, index: 4 },
+            { chunk: chunk, index: 5 }
+        ];
+        return Promise.all(data.map(function (value) {
+            pool.enqueue(value);
+            expect(pool.processing.length).toBeLessThanOrEqual(2);
+        })).then(function () {
+            expect(m.mock.calls.length).toBe(6);
+        });
+    });
+});
+//# sourceMappingURL=pool.test.js.map

+ 1 - 0
qiniu-js/esm/__tests__/pool.test.js.map

@@ -0,0 +1 @@
+{"version":3,"file":"pool.test.js","sourceRoot":"","sources":["../../src/__tests__/pool.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAG9B,IAAM,CAAC,GAAG,IAAI,CAAC,EAAE,EAAE,CAAA;AACnB,IAAM,IAAI,GAAG;IACX,OAAO,IAAI,OAAO,CAAC,UAAC,OAAO,EAAE,CAAC;QAC5B,CAAC,EAAE,CAAA;QACH,OAAO,EAAE,CAAA;IACX,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA;AAED,QAAQ,CAAC,mCAAmC,EAAE;IAC5C,IAAI,IAAI,GAAG,IAAI,IAAI,CAAY,IAAI,EAAE,CAAC,CAAC,CAAA;IACvC,IAAI,CAAC,SAAS,EAAE;QACd,IAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAA;QACxB,IAAM,IAAI,GAAG;YACX,EAAE,KAAK,OAAA,EAAE,KAAK,EAAE,CAAC,EAAE;YACnB,EAAE,KAAK,OAAA,EAAE,KAAK,EAAE,CAAC,EAAE;YACnB,EAAE,KAAK,OAAA,EAAE,KAAK,EAAE,CAAC,EAAE;YACnB,EAAE,KAAK,OAAA,EAAE,KAAK,EAAE,CAAC,EAAE;YACnB,EAAE,KAAK,OAAA,EAAE,KAAK,EAAE,CAAC,EAAE;YACnB,EAAE,KAAK,OAAA,EAAE,KAAK,EAAE,CAAC,EAAE;SACpB,CAAA;QACD,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,UAAA,KAAK;YAC/B,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;YACnB,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAA;QACvD,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACP,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACrC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}

+ 1 - 0
qiniu-js/esm/__tests__/utils.test.d.ts

@@ -0,0 +1 @@
+export {};

+ 8 - 0
qiniu-js/esm/__tests__/utils.test.js

@@ -0,0 +1,8 @@
+import { createLocalKey } from '../utils';
+describe('api function test', function () {
+    test('createLocalKey', function () {
+        expect(createLocalKey('test', null, 1024)).toMatch('qiniu_js_sdk_upload_file_name_test_size_1024');
+        expect(createLocalKey('test', 'demo', 1024)).toMatch('qiniu_js_sdk_upload_file_name_test_key_demo_size_1024');
+    });
+});
+//# sourceMappingURL=utils.test.js.map

+ 1 - 0
qiniu-js/esm/__tests__/utils.test.js.map

@@ -0,0 +1 @@
+{"version":3,"file":"utils.test.js","sourceRoot":"","sources":["../../src/__tests__/utils.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAEzC,QAAQ,CAAC,mBAAmB,EAAE;IAC5B,IAAI,CAAC,gBAAgB,EAAE;QACrB,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,8CAA8C,CAAC,CAAA;QAClG,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,uDAAuD,CAAC,CAAA;IAC/G,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}

+ 54 - 0
qiniu-js/esm/api.d.ts

@@ -0,0 +1,54 @@
+import * as utils from './utils';
+import { Config, UploadInfo } from './upload';
+interface UpHosts {
+    data: {
+        up: {
+            acc: {
+                main: string[];
+            };
+        };
+    };
+}
+export declare function getUpHosts(token: string, protocol: 'https:' | 'http:'): Promise<UpHosts>;
+export declare type UploadUrlConfig = Partial<Pick<Config, 'upprotocol' | 'uphost' | 'region' | 'useCdnDomain'>>;
+/** 获取上传url */
+export declare function getUploadUrl(config: UploadUrlConfig, token: string): Promise<string>;
+export interface InitPartsData {
+    /** 该文件的上传 id, 后续该文件其他各个块的上传,已上传块的废弃,已上传块的合成文件,都需要该 id */
+    uploadId: string;
+    /** uploadId 的过期时间 */
+    expireAt: number;
+}
+/**
+ * @param token 上传鉴权凭证
+ * @param bucket 上传空间
+ * @param key 目标文件名
+ * @param uploadUrl 上传地址
+ */
+export declare function initUploadParts(token: string, bucket: string, key: string | null | undefined, uploadUrl: string): utils.Response<InitPartsData>;
+export interface UploadChunkData {
+    etag: string;
+    md5: string;
+}
+/**
+ * @param token 上传鉴权凭证
+ * @param index 当前 chunk 的索引
+ * @param uploadInfo 上传信息
+ * @param options 请求参数
+ */
+export declare function uploadChunk(token: string, key: string | null | undefined, index: number, uploadInfo: UploadInfo, options: Partial<utils.RequestOptions>): utils.Response<UploadChunkData>;
+export declare type UploadCompleteData = any;
+/**
+ * @param token 上传鉴权凭证
+ * @param key 目标文件名
+ * @param uploadInfo 上传信息
+ * @param options 请求参数
+ */
+export declare function uploadComplete(token: string, key: string | null | undefined, uploadInfo: UploadInfo, options: Partial<utils.RequestOptions>): utils.Response<UploadCompleteData>;
+/**
+ * @param token 上传鉴权凭证
+ * @param key 目标文件名
+ * @param uploadInfo 上传信息
+ */
+export declare function deleteUploadedChunks(token: string, key: string | null | undefined, uploadinfo: UploadInfo): utils.Response<void>;
+export {};

+ 143 - 0
qiniu-js/esm/api.js

@@ -0,0 +1,143 @@
+var __assign = (this && this.__assign) || function () {
+    __assign = Object.assign || function(t) {
+        for (var s, i = 1, n = arguments.length; i < n; i++) {
+            s = arguments[i];
+            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
+                t[p] = s[p];
+        }
+        return t;
+    };
+    return __assign.apply(this, arguments);
+};
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+    function verb(n) { return function (v) { return step([n, v]); }; }
+    function step(op) {
+        if (f) throw new TypeError("Generator is already executing.");
+        while (_) try {
+            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+            if (y = 0, t) op = [op[0] & 2, t.value];
+            switch (op[0]) {
+                case 0: case 1: t = op; break;
+                case 4: _.label++; return { value: op[1], done: false };
+                case 5: _.label++; y = op[1]; op = [0]; continue;
+                case 7: op = _.ops.pop(); _.trys.pop(); continue;
+                default:
+                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+                    if (t[2]) _.ops.pop();
+                    _.trys.pop(); continue;
+            }
+            op = body.call(thisArg, _);
+        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+    }
+};
+import * as utils from './utils';
+import { regionUphostMap } from './config';
+import { urlSafeBase64Encode } from './base64';
+export function getUpHosts(token, protocol) {
+    return __awaiter(this, void 0, void 0, function () {
+        var putPolicy, url;
+        return __generator(this, function (_a) {
+            putPolicy = utils.getPutPolicy(token);
+            url = protocol + '//api.qiniu.com/v2/query?ak=' + putPolicy.ak + '&bucket=' + putPolicy.bucket;
+            return [2 /*return*/, utils.request(url, { method: 'GET' })];
+        });
+    });
+}
+/** 获取上传url */
+export function getUploadUrl(config, token) {
+    return __awaiter(this, void 0, void 0, function () {
+        var protocol, upHosts, host, res, hosts;
+        return __generator(this, function (_a) {
+            switch (_a.label) {
+                case 0:
+                    protocol = config.upprotocol || 'https:';
+                    if (config.uphost) {
+                        return [2 /*return*/, protocol + "//" + config.uphost];
+                    }
+                    if (config.region) {
+                        upHosts = regionUphostMap[config.region];
+                        host = config.useCdnDomain ? upHosts.cdnUphost : upHosts.srcUphost;
+                        return [2 /*return*/, protocol + "//" + host];
+                    }
+                    return [4 /*yield*/, getUpHosts(token, protocol)];
+                case 1:
+                    res = _a.sent();
+                    hosts = res.data.up.acc.main;
+                    return [2 /*return*/, protocol + "//" + hosts[0]];
+            }
+        });
+    });
+}
+/**
+ * @param bucket 空间名
+ * @param key 目标文件名
+ * @param uploadInfo 上传信息
+ */
+function getBaseUrl(bucket, key, uploadInfo) {
+    var url = uploadInfo.url, id = uploadInfo.id;
+    return url + "/buckets/" + bucket + "/objects/" + (key != null ? urlSafeBase64Encode(key) : '~') + "/uploads/" + id;
+}
+/**
+ * @param token 上传鉴权凭证
+ * @param bucket 上传空间
+ * @param key 目标文件名
+ * @param uploadUrl 上传地址
+ */
+export function initUploadParts(token, bucket, key, uploadUrl) {
+    var url = uploadUrl + "/buckets/" + bucket + "/objects/" + (key != null ? urlSafeBase64Encode(key) : '~') + "/uploads";
+    return utils.request(url, {
+        method: 'POST',
+        headers: utils.getAuthHeaders(token)
+    });
+}
+/**
+ * @param token 上传鉴权凭证
+ * @param index 当前 chunk 的索引
+ * @param uploadInfo 上传信息
+ * @param options 请求参数
+ */
+export function uploadChunk(token, key, index, uploadInfo, options) {
+    var bucket = utils.getPutPolicy(token).bucket;
+    var url = getBaseUrl(bucket, key, uploadInfo) + ("/" + index);
+    return utils.request(url, __assign(__assign({}, options), { method: 'PUT', headers: utils.getHeadersForChunkUpload(token) }));
+}
+/**
+ * @param token 上传鉴权凭证
+ * @param key 目标文件名
+ * @param uploadInfo 上传信息
+ * @param options 请求参数
+ */
+export function uploadComplete(token, key, uploadInfo, options) {
+    var bucket = utils.getPutPolicy(token).bucket;
+    var url = getBaseUrl(bucket, key, uploadInfo);
+    return utils.request(url, __assign(__assign({}, options), { method: 'POST', headers: utils.getHeadersForMkFile(token) }));
+}
+/**
+ * @param token 上传鉴权凭证
+ * @param key 目标文件名
+ * @param uploadInfo 上传信息
+ */
+export function deleteUploadedChunks(token, key, uploadinfo) {
+    var bucket = utils.getPutPolicy(token).bucket;
+    var url = getBaseUrl(bucket, key, uploadinfo);
+    return utils.request(url, {
+        method: 'DELETE',
+        headers: utils.getAuthHeaders(token)
+    });
+}
+//# sourceMappingURL=api.js.map

Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
qiniu-js/esm/api.js.map


+ 5 - 0
qiniu-js/esm/base64.d.ts

@@ -0,0 +1,5 @@
+export declare function utf8Encode(argString: any): string;
+export declare function base64Encode(data: any): any;
+export declare function base64Decode(data: string): string;
+export declare function urlSafeBase64Encode(v: any): any;
+export declare function urlSafeBase64Decode(v: any): string;

+ 162 - 0
qiniu-js/esm/base64.js

@@ -0,0 +1,162 @@
+/* eslint-disable */
+export function utf8Encode(argString) {
+    // http://kevin.vanzonneveld.net
+    // +   original by: Webtoolkit.info (http://www.webtoolkit.info/)
+    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+    // +   improved by: sowberry
+    // +    tweaked by: Jack
+    // +   bugfixed by: Onno Marsman
+    // +   improved by: Yves Sucaet
+    // +   bugfixed by: Onno Marsman
+    // +   bugfixed by: Ulrich
+    // +   bugfixed by: Rafal Kukawski
+    // +   improved by: kirilloid
+    // +   bugfixed by: kirilloid
+    // *     example 1: this.utf8Encode('Kevin van Zonneveld')
+    // *     returns 1: 'Kevin van Zonneveld'
+    if (argString === null || typeof argString === 'undefined') {
+        return '';
+    }
+    var string = argString + ''; // .replace(/\r\n/g, '\n').replace(/\r/g, '\n')
+    var utftext = '', start, end, stringl = 0;
+    start = end = 0;
+    stringl = string.length;
+    for (var n = 0; n < stringl; n++) {
+        var c1 = string.charCodeAt(n);
+        var enc = null;
+        if (c1 < 128) {
+            end++;
+        }
+        else if (c1 > 127 && c1 < 2048) {
+            enc = String.fromCharCode((c1 >> 6) | 192, (c1 & 63) | 128);
+        }
+        else if ((c1 & 0xf800 ^ 0xd800) > 0) {
+            enc = String.fromCharCode((c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128);
+        }
+        else {
+            // surrogate pairs
+            if ((c1 & 0xfc00 ^ 0xd800) > 0) {
+                throw new RangeError('Unmatched trail surrogate at ' + n);
+            }
+            var c2 = string.charCodeAt(++n);
+            if ((c2 & 0xfc00 ^ 0xdc00) > 0) {
+                throw new RangeError('Unmatched lead surrogate at ' + (n - 1));
+            }
+            c1 = ((c1 & 0x3ff) << 10) + (c2 & 0x3ff) + 0x10000;
+            enc = String.fromCharCode((c1 >> 18) | 240, ((c1 >> 12) & 63) | 128, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128);
+        }
+        if (enc !== null) {
+            if (end > start) {
+                utftext += string.slice(start, end);
+            }
+            utftext += enc;
+            start = end = n + 1;
+        }
+    }
+    if (end > start) {
+        utftext += string.slice(start, stringl);
+    }
+    return utftext;
+}
+export function base64Encode(data) {
+    // http://kevin.vanzonneveld.net
+    // +   original by: Tyler Akins (http://rumkin.com)
+    // +   improved by: Bayron Guevara
+    // +   improved by: Thunder.m
+    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+    // +   bugfixed by: Pellentesque Malesuada
+    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+    // -    depends on: this.utf8Encode
+    // *     example 1: this.base64Encode('Kevin van Zonneveld')
+    // *     returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
+    // mozilla has this native
+    // - but breaks in 2.0.0.12!
+    // if (typeof this.window['atob'] == 'function') {
+    //    return atob(data)
+    // }
+    var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
+    var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, enc = '', tmp_arr = [];
+    if (!data) {
+        return data;
+    }
+    data = utf8Encode(data + '');
+    do {
+        // pack three octets into four hexets
+        o1 = data.charCodeAt(i++);
+        o2 = data.charCodeAt(i++);
+        o3 = data.charCodeAt(i++);
+        bits = (o1 << 16) | (o2 << 8) | o3;
+        h1 = (bits >> 18) & 0x3f;
+        h2 = (bits >> 12) & 0x3f;
+        h3 = (bits >> 6) & 0x3f;
+        h4 = bits & 0x3f;
+        // use hexets to index into b64, and append result to encoded string
+        tmp_arr[ac++] =
+            b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
+    } while (i < data.length);
+    enc = tmp_arr.join('');
+    switch (data.length % 3) {
+        case 1:
+            enc = enc.slice(0, -2) + '==';
+            break;
+        case 2:
+            enc = enc.slice(0, -1) + '=';
+            break;
+    }
+    return enc;
+}
+export function base64Decode(data) {
+    // http://kevin.vanzonneveld.net
+    // +   original by: Tyler Akins (http://rumkin.com)
+    // +   improved by: Thunder.m
+    // +      input by: Aman Gupta
+    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+    // +   bugfixed by: Onno Marsman
+    // +   bugfixed by: Pellentesque Malesuada
+    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+    // +      input by: Brett Zamir (http://brett-zamir.me)
+    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+    // *     example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==')
+    // *     returns 1: 'Kevin van Zonneveld'
+    // mozilla has this native
+    // - but breaks in 2.0.0.12!
+    // if (typeof this.window['atob'] == 'function') {
+    //    return atob(data)
+    // }
+    var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
+    var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, dec = '', tmp_arr = [];
+    if (!data) {
+        return data;
+    }
+    data += '';
+    do { // unpack four hexets into three octets using index points in b64
+        h1 = b64.indexOf(data.charAt(i++));
+        h2 = b64.indexOf(data.charAt(i++));
+        h3 = b64.indexOf(data.charAt(i++));
+        h4 = b64.indexOf(data.charAt(i++));
+        bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
+        o1 = bits >> 16 & 0xff;
+        o2 = bits >> 8 & 0xff;
+        o3 = bits & 0xff;
+        if (h3 === 64) {
+            tmp_arr[ac++] = String.fromCharCode(o1);
+        }
+        else if (h4 === 64) {
+            tmp_arr[ac++] = String.fromCharCode(o1, o2);
+        }
+        else {
+            tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
+        }
+    } while (i < data.length);
+    dec = tmp_arr.join('');
+    return dec;
+}
+export function urlSafeBase64Encode(v) {
+    v = base64Encode(v);
+    return v.replace(/\//g, '_').replace(/\+/g, '-');
+}
+export function urlSafeBase64Decode(v) {
+    v = v.replace(/_/g, '/').replace(/-/g, '+');
+    return base64Decode(v);
+}
+//# sourceMappingURL=base64.js.map

Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
qiniu-js/esm/base64.js.map


+ 17 - 0
qiniu-js/esm/compress.d.ts

@@ -0,0 +1,17 @@
+export interface CompressOptions {
+    quality?: number;
+    noCompressIfLarger?: boolean;
+    maxWidth?: number;
+    maxHeight?: number;
+}
+export interface Dimension {
+    width?: number;
+    height?: number;
+}
+export interface CompressResult {
+    dist: Blob | File;
+    width: number;
+    height: number;
+}
+declare const compressImage: (file: File, options: CompressOptions) => Promise<CompressResult>;
+export default compressImage;

+ 248 - 0
qiniu-js/esm/compress.js

@@ -0,0 +1,248 @@
+var __assign = (this && this.__assign) || function () {
+    __assign = Object.assign || function(t) {
+        for (var s, i = 1, n = arguments.length; i < n; i++) {
+            s = arguments[i];
+            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
+                t[p] = s[p];
+        }
+        return t;
+    };
+    return __assign.apply(this, arguments);
+};
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+    function verb(n) { return function (v) { return step([n, v]); }; }
+    function step(op) {
+        if (f) throw new TypeError("Generator is already executing.");
+        while (_) try {
+            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+            if (y = 0, t) op = [op[0] & 2, t.value];
+            switch (op[0]) {
+                case 0: case 1: t = op; break;
+                case 4: _.label++; return { value: op[1], done: false };
+                case 5: _.label++; y = op[1]; op = [0]; continue;
+                case 7: op = _.ops.pop(); _.trys.pop(); continue;
+                default:
+                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+                    if (t[2]) _.ops.pop();
+                    _.trys.pop(); continue;
+            }
+            op = body.call(thisArg, _);
+        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+    }
+};
+var __read = (this && this.__read) || function (o, n) {
+    var m = typeof Symbol === "function" && o[Symbol.iterator];
+    if (!m) return o;
+    var i = m.call(o), r, ar = [], e;
+    try {
+        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
+    }
+    catch (error) { e = { error: error }; }
+    finally {
+        try {
+            if (r && !r.done && (m = i["return"])) m.call(i);
+        }
+        finally { if (e) throw e.error; }
+    }
+    return ar;
+};
+var __spread = (this && this.__spread) || function () {
+    for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
+    return ar;
+};
+import { EXIF } from 'exif-js';
+import { createObjectURL, getTransform } from './utils';
+var mimeTypes = {
+    PNG: 'image/png',
+    JPEG: 'image/jpeg',
+    WEBP: 'image/webp',
+    BMP: 'image/bmp'
+};
+var maxSteps = 4;
+var scaleFactor = Math.log(2);
+var supportMimeTypes = Object.keys(mimeTypes).map(function (type) { return mimeTypes[type]; });
+var defaultType = mimeTypes.JPEG;
+function isSupportedType(type) {
+    return supportMimeTypes.includes(type);
+}
+var Compress = /** @class */ (function () {
+    function Compress(file, config) {
+        this.file = file;
+        this.config = config;
+        this.config = __assign({ quality: 0.92, noCompressIfLarger: false }, this.config);
+    }
+    Compress.prototype.process = function () {
+        return __awaiter(this, void 0, void 0, function () {
+            var srcDimension, originImage, canvas, scale, scaleCanvas, distBlob;
+            return __generator(this, function (_a) {
+                switch (_a.label) {
+                    case 0:
+                        this.outputType = this.file.type;
+                        srcDimension = {};
+                        if (!isSupportedType(this.file.type)) {
+                            throw new Error("unsupported file type: " + this.file.type);
+                        }
+                        return [4 /*yield*/, this.getOriginImage()];
+                    case 1:
+                        originImage = _a.sent();
+                        return [4 /*yield*/, this.getCanvas(originImage)];
+                    case 2:
+                        canvas = _a.sent();
+                        scale = 1;
+                        if (this.config.maxWidth) {
+                            scale = Math.min(1, this.config.maxWidth / canvas.width);
+                        }
+                        if (this.config.maxHeight) {
+                            scale = Math.min(1, scale, this.config.maxHeight / canvas.height);
+                        }
+                        srcDimension.width = canvas.width;
+                        srcDimension.height = canvas.height;
+                        return [4 /*yield*/, this.doScale(canvas, scale)];
+                    case 3:
+                        scaleCanvas = _a.sent();
+                        distBlob = this.toBlob(scaleCanvas);
+                        if (distBlob.size > this.file.size && this.config.noCompressIfLarger) {
+                            return [2 /*return*/, {
+                                    dist: this.file,
+                                    width: srcDimension.width,
+                                    height: srcDimension.height
+                                }];
+                        }
+                        return [2 /*return*/, {
+                                dist: distBlob,
+                                width: scaleCanvas.width,
+                                height: scaleCanvas.height
+                            }];
+                }
+            });
+        });
+    };
+    Compress.prototype.clear = function (ctx, width, height) {
+        // jpeg 没有 alpha 通道,透明区间会被填充成黑色,这里把透明区间填充为白色
+        if (this.outputType === defaultType) {
+            ctx.fillStyle = '#fff';
+            ctx.fillRect(0, 0, width, height);
+        }
+        else {
+            ctx.clearRect(0, 0, width, height);
+        }
+    };
+    /** 通过 file 初始化 image 对象 */
+    Compress.prototype.getOriginImage = function () {
+        var _this = this;
+        return new Promise(function (resolve, reject) {
+            var url = createObjectURL(_this.file);
+            var img = new Image();
+            img.onload = function () {
+                resolve(img);
+            };
+            img.onerror = function () {
+                reject('image load error');
+            };
+            img.src = url;
+        });
+    };
+    Compress.prototype.getCanvas = function (img) {
+        var _this = this;
+        return new Promise(function (resolve, reject) {
+            // 通过得到图片的信息来调整显示方向以正确显示图片,主要解决 ios 系统上的图片会有旋转的问题
+            EXIF.getData(img, function () {
+                var orientation = EXIF.getTag(img, 'Orientation') || 1;
+                var _a = getTransform(img, orientation), width = _a.width, height = _a.height, matrix = _a.matrix;
+                var canvas = document.createElement('canvas');
+                var context = canvas.getContext('2d');
+                canvas.width = width;
+                canvas.height = height;
+                if (!context) {
+                    reject(new Error('context is null'));
+                    return;
+                }
+                _this.clear(context, width, height);
+                context.transform.apply(context, __spread(matrix));
+                context.drawImage(img, 0, 0);
+                resolve(canvas);
+            });
+        });
+    };
+    Compress.prototype.doScale = function (source, scale) {
+        return __awaiter(this, void 0, void 0, function () {
+            var sctx, steps, factor, mirror, mctx, width, height, originWidth, originHeight, src, context, i, dw, dh, canvas, data;
+            return __generator(this, function (_a) {
+                if (scale === 1) {
+                    return [2 /*return*/, source];
+                }
+                sctx = source.getContext('2d');
+                steps = Math.min(maxSteps, Math.ceil((1 / scale) / scaleFactor));
+                factor = Math.pow(scale, (1 / steps));
+                mirror = document.createElement('canvas');
+                mctx = mirror.getContext('2d');
+                width = source.width, height = source.height;
+                originWidth = width;
+                originHeight = height;
+                mirror.width = width;
+                mirror.height = height;
+                if (!mctx || !sctx) {
+                    throw new Error("mctx or sctx can't be null");
+                }
+                for (i = 0; i < steps; i++) {
+                    dw = width * factor | 0 // eslint-disable-line no-bitwise
+                    ;
+                    dh = height * factor | 0 // eslint-disable-line no-bitwise
+                    ;
+                    // 到最后一步的时候 dw, dh 用目标缩放尺寸,否则会出现最后尺寸偏小的情况
+                    if (i === steps - 1) {
+                        dw = originWidth * scale;
+                        dh = originHeight * scale;
+                    }
+                    if (i % 2 === 0) {
+                        src = source;
+                        context = mctx;
+                    }
+                    else {
+                        src = mirror;
+                        context = sctx;
+                    }
+                    // 每次画前都清空,避免图像重叠
+                    this.clear(context, width, height);
+                    context.drawImage(src, 0, 0, width, height, 0, 0, dw, dh);
+                    width = dw;
+                    height = dh;
+                }
+                canvas = src === source ? mirror : source;
+                data = context.getImageData(0, 0, width, height);
+                // resize
+                canvas.width = width;
+                canvas.height = height;
+                // store image data
+                context.putImageData(data, 0, 0);
+                return [2 /*return*/, canvas];
+            });
+        });
+    };
+    /** 这里把 base64 字符串转为 blob 对象 */
+    Compress.prototype.toBlob = function (result) {
+        var dataURL = result.toDataURL(this.outputType, this.config.quality);
+        var buffer = atob(dataURL.split(',')[1]).split('').map(function (char) { return char.charCodeAt(0); });
+        var blob = new Blob([new Uint8Array(buffer)], { type: this.outputType });
+        return blob;
+    };
+    return Compress;
+}());
+var compressImage = function (file, options) { return new Compress(file, options).process(); };
+export default compressImage;
+//# sourceMappingURL=compress.js.map

Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
qiniu-js/esm/compress.js.map


+ 31 - 0
qiniu-js/esm/config.d.ts

@@ -0,0 +1,31 @@
+/** 上传区域 */
+export declare const region: {
+    readonly z0: "z0";
+    readonly z1: "z1";
+    readonly z2: "z2";
+    readonly na0: "na0";
+    readonly as0: "as0";
+};
+/** 上传区域对应的 host */
+export declare const regionUphostMap: {
+    readonly z0: {
+        readonly srcUphost: "up.qiniup.com";
+        readonly cdnUphost: "upload.qiniup.com";
+    };
+    readonly z1: {
+        readonly srcUphost: "up-z1.qiniup.com";
+        readonly cdnUphost: "upload-z1.qiniup.com";
+    };
+    readonly z2: {
+        readonly srcUphost: "up-z2.qiniup.com";
+        readonly cdnUphost: "upload-z2.qiniup.com";
+    };
+    readonly na0: {
+        readonly srcUphost: "up-na0.qiniup.com";
+        readonly cdnUphost: "upload-na0.qiniup.com";
+    };
+    readonly as0: {
+        readonly srcUphost: "up-as0.qiniup.com";
+        readonly cdnUphost: "upload-as0.qiniup.com";
+    };
+};

+ 33 - 0
qiniu-js/esm/config.js

@@ -0,0 +1,33 @@
+var _a;
+/** 上传区域 */
+export var region = {
+    z0: 'z0',
+    z1: 'z1',
+    z2: 'z2',
+    na0: 'na0',
+    as0: 'as0'
+};
+/** 上传区域对应的 host */
+export var regionUphostMap = (_a = {},
+    _a[region.z0] = {
+        srcUphost: 'up.qiniup.com',
+        cdnUphost: 'upload.qiniup.com'
+    },
+    _a[region.z1] = {
+        srcUphost: 'up-z1.qiniup.com',
+        cdnUphost: 'upload-z1.qiniup.com'
+    },
+    _a[region.z2] = {
+        srcUphost: 'up-z2.qiniup.com',
+        cdnUphost: 'upload-z2.qiniup.com'
+    },
+    _a[region.na0] = {
+        srcUphost: 'up-na0.qiniup.com',
+        cdnUphost: 'upload-na0.qiniup.com'
+    },
+    _a[region.as0] = {
+        srcUphost: 'up-as0.qiniup.com',
+        cdnUphost: 'upload-as0.qiniup.com'
+    },
+    _a);
+//# sourceMappingURL=config.js.map

+ 1 - 0
qiniu-js/esm/config.js.map

@@ -0,0 +1 @@
+{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";AAAA,WAAW;AACX,MAAM,CAAC,IAAM,MAAM,GAAG;IACpB,EAAE,EAAE,IAAI;IACR,EAAE,EAAE,IAAI;IACR,EAAE,EAAE,IAAI;IACR,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;CACF,CAAA;AAEV,mBAAmB;AACnB,MAAM,CAAC,IAAM,eAAe,GAAG,CAAA;IAC7B,GAAC,MAAM,CAAC,EAAE,IAAG;QACX,SAAS,EAAE,eAAe;QAC1B,SAAS,EAAE,mBAAmB;KAC/B;IACD,GAAC,MAAM,CAAC,EAAE,IAAG;QACX,SAAS,EAAE,kBAAkB;QAC7B,SAAS,EAAE,sBAAsB;KAClC;IACD,GAAC,MAAM,CAAC,EAAE,IAAG;QACX,SAAS,EAAE,kBAAkB;QAC7B,SAAS,EAAE,sBAAsB;KAClC;IACD,GAAC,MAAM,CAAC,GAAG,IAAG;QACZ,SAAS,EAAE,mBAAmB;QAC9B,SAAS,EAAE,uBAAuB;KACnC;IACD,GAAC,MAAM,CAAC,GAAG,IAAG;QACZ,SAAS,EAAE,mBAAmB;QAC9B,SAAS,EAAE,uBAAuB;KACnC;MACO,CAAA,CAAA"}

+ 48 - 0
qiniu-js/esm/image.d.ts

@@ -0,0 +1,48 @@
+export interface ImageViewOptions {
+    mode: number;
+    format?: string;
+    w?: number;
+    h?: number;
+    q?: number;
+}
+export interface ImageWatermark {
+    image: string;
+    mode: number;
+    fontsize?: number;
+    dissolve?: number;
+    dx?: number;
+    dy?: number;
+    gravity?: string;
+    text?: string;
+    font?: string;
+    fill?: string;
+}
+export interface ImageMogr2 {
+    'auto-orient'?: boolean;
+    strip?: boolean;
+    thumbnail?: number;
+    crop?: number;
+    gravity?: number;
+    format?: number;
+    blur?: number;
+    quality?: number;
+    rotate?: number;
+}
+declare type Pipeline = (ImageWatermark & {
+    fop: 'watermark';
+}) | (ImageViewOptions & {
+    fop: 'imageView2';
+}) | (ImageMogr2 & {
+    fop: 'imageMogr2';
+});
+export interface Entry {
+    domain: string;
+    key: string;
+}
+export declare function imageView2(op: ImageViewOptions, key?: string, domain?: string): string;
+export declare function imageMogr2(op: ImageMogr2, key?: string, domain?: string): string;
+export declare function watermark(op: ImageWatermark, key?: string, domain?: string): string;
+export declare function imageInfo(key: string, domain: string): import("./utils").Response<unknown>;
+export declare function exif(key: string, domain: string): import("./utils").Response<unknown>;
+export declare function pipeline(arr: Pipeline[], key?: string, domain?: string): string;
+export {};

+ 134 - 0
qiniu-js/esm/image.js

@@ -0,0 +1,134 @@
+import { request } from './utils';
+import { urlSafeBase64Encode } from './base64';
+function getImageUrl(key, domain) {
+    key = encodeURIComponent(key);
+    if (domain.slice(domain.length - 1) !== '/') {
+        domain += '/';
+    }
+    return domain + key;
+}
+export function imageView2(op, key, domain) {
+    if (!/^\d$/.test(String(op.mode))) {
+        throw 'mode should be number in imageView2';
+    }
+    var mode = op.mode, w = op.w, h = op.h, q = op.q, format = op.format;
+    if (!w && !h) {
+        throw 'param w and h is empty in imageView2';
+    }
+    var imageUrl = 'imageView2/' + encodeURIComponent(mode);
+    imageUrl += w ? '/w/' + encodeURIComponent(w) : '';
+    imageUrl += h ? '/h/' + encodeURIComponent(h) : '';
+    imageUrl += q ? '/q/' + encodeURIComponent(q) : '';
+    imageUrl += format ? '/format/' + encodeURIComponent(format) : '';
+    if (key && domain) {
+        imageUrl = getImageUrl(key, domain) + '?' + imageUrl;
+    }
+    return imageUrl;
+}
+// invoke the imageMogr2 api of Qiniu
+export function imageMogr2(op, key, domain) {
+    var autoOrient = op['auto-orient'];
+    var thumbnail = op.thumbnail, strip = op.strip, gravity = op.gravity, crop = op.crop, quality = op.quality, rotate = op.rotate, format = op.format, blur = op.blur;
+    var imageUrl = 'imageMogr2';
+    imageUrl += autoOrient ? '/auto-orient' : '';
+    imageUrl += thumbnail ? '/thumbnail/' + encodeURIComponent(thumbnail) : '';
+    imageUrl += strip ? '/strip' : '';
+    imageUrl += gravity ? '/gravity/' + encodeURIComponent(gravity) : '';
+    imageUrl += quality ? '/quality/' + encodeURIComponent(quality) : '';
+    imageUrl += crop ? '/crop/' + encodeURIComponent(crop) : '';
+    imageUrl += rotate ? '/rotate/' + encodeURIComponent(rotate) : '';
+    imageUrl += format ? '/format/' + encodeURIComponent(format) : '';
+    imageUrl += blur ? '/blur/' + encodeURIComponent(blur) : '';
+    if (key && domain) {
+        imageUrl = getImageUrl(key, domain) + '?' + imageUrl;
+    }
+    return imageUrl;
+}
+// invoke the watermark api of Qiniu
+export function watermark(op, key, domain) {
+    var mode = op.mode;
+    if (!mode) {
+        throw "mode can't be empty in watermark";
+    }
+    var imageUrl = 'watermark/' + mode;
+    if (mode !== 1 && mode !== 2) {
+        throw 'mode is wrong';
+    }
+    if (mode === 1) {
+        var image = op.image;
+        if (!image) {
+            throw "image can't be empty in watermark";
+        }
+        imageUrl += image ? '/image/' + urlSafeBase64Encode(image) : '';
+    }
+    if (mode === 2) {
+        var text = op.text, font = op.font, fontsize = op.fontsize, fill = op.fill;
+        if (!text) {
+            throw "text can't be empty in watermark";
+        }
+        imageUrl += text ? '/text/' + urlSafeBase64Encode(text) : '';
+        imageUrl += font ? '/font/' + urlSafeBase64Encode(font) : '';
+        imageUrl += fontsize ? '/fontsize/' + fontsize : '';
+        imageUrl += fill ? '/fill/' + urlSafeBase64Encode(fill) : '';
+    }
+    var dissolve = op.dissolve, gravity = op.gravity, dx = op.dx, dy = op.dy;
+    imageUrl += dissolve ? '/dissolve/' + encodeURIComponent(dissolve) : '';
+    imageUrl += gravity ? '/gravity/' + encodeURIComponent(gravity) : '';
+    imageUrl += dx ? '/dx/' + encodeURIComponent(dx) : '';
+    imageUrl += dy ? '/dy/' + encodeURIComponent(dy) : '';
+    if (key && domain) {
+        imageUrl = getImageUrl(key, domain) + '?' + imageUrl;
+    }
+    return imageUrl;
+}
+// invoke the imageInfo api of Qiniu
+export function imageInfo(key, domain) {
+    var url = getImageUrl(key, domain) + '?imageInfo';
+    return request(url, { method: 'GET' });
+}
+// invoke the exif api of Qiniu
+export function exif(key, domain) {
+    var url = getImageUrl(key, domain) + '?exif';
+    return request(url, { method: 'GET' });
+}
+export function pipeline(arr, key, domain) {
+    var isArray = Object.prototype.toString.call(arr) === '[object Array]';
+    var option;
+    var errOp = false;
+    var imageUrl = '';
+    if (isArray) {
+        for (var i = 0, len = arr.length; i < len; i++) {
+            option = arr[i];
+            if (!option.fop) {
+                throw "fop can't be empty in pipeline";
+            }
+            switch (option.fop) {
+                case 'watermark':
+                    imageUrl += watermark(option) + '|';
+                    break;
+                case 'imageView2':
+                    imageUrl += imageView2(option) + '|';
+                    break;
+                case 'imageMogr2':
+                    imageUrl += imageMogr2(option) + '|';
+                    break;
+                default:
+                    errOp = true;
+                    break;
+            }
+            if (errOp) {
+                throw 'fop is wrong in pipeline';
+            }
+        }
+        if (key && domain) {
+            imageUrl = getImageUrl(key, domain) + '?' + imageUrl;
+            var length_1 = imageUrl.length;
+            if (imageUrl.slice(length_1 - 1) === '|') {
+                imageUrl = imageUrl.slice(0, length_1 - 1);
+            }
+        }
+        return imageUrl;
+    }
+    throw "pipeline's first param should be array";
+}
+//# sourceMappingURL=image.js.map

Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
qiniu-js/esm/image.js.map


+ 21 - 0
qiniu-js/esm/index.d.ts

@@ -0,0 +1,21 @@
+import { Extra, Config, UploadProgress } from './upload';
+import { Observable } from './observable';
+import { CustomError } from './utils';
+import { UploadCompleteData } from './api';
+import compressImage from './compress';
+/**
+ * @param file 上传文件
+ * @param key 目标文件名
+ * @param token 上传凭证
+ * @param putExtra 上传文件的相关资源信息配置
+ * @param config 上传任务的配置
+ * @returns 返回用于上传任务的可观察对象
+ */
+declare function upload(file: File, key: string | null | undefined, token: string, putExtra?: Partial<Extra>, config?: Partial<Config>): Observable<UploadProgress, CustomError, UploadCompleteData>;
+export { getHeadersForMkFile, getHeadersForChunkUpload } from './utils';
+export { urlSafeBase64Encode, urlSafeBase64Decode } from './base64';
+export { CompressResult } from './compress';
+export { deleteUploadedChunks, getUploadUrl } from './api';
+export { imageMogr2, watermark, imageInfo, exif, pipeline } from './image';
+export { region } from './config';
+export { upload, compressImage };

+ 39 - 0
qiniu-js/esm/index.js

@@ -0,0 +1,39 @@
+import createUploadManager from './upload';
+import { Observable } from './observable';
+import compressImage from './compress';
+import Logger from './logger';
+/**
+ * @param file 上传文件
+ * @param key 目标文件名
+ * @param token 上传凭证
+ * @param putExtra 上传文件的相关资源信息配置
+ * @param config 上传任务的配置
+ * @returns 返回用于上传任务的可观察对象
+ */
+function upload(file, key, token, putExtra, config) {
+    var options = {
+        file: file,
+        key: key,
+        token: token,
+        putExtra: putExtra,
+        config: config
+    };
+    // 为每个任务创建单独的 Logger
+    var logger = new Logger(token, config === null || config === void 0 ? void 0 : config.disableStatisticsReport, config === null || config === void 0 ? void 0 : config.debugLogLevel);
+    return new Observable(function (observer) {
+        var manager = createUploadManager(options, {
+            onData: function (data) { return observer.next(data); },
+            onError: function (err) { return observer.error(err); },
+            onComplete: function (res) { return observer.complete(res); }
+        }, logger);
+        manager.putFile();
+        return manager.stop.bind(manager);
+    });
+}
+export { getHeadersForMkFile, getHeadersForChunkUpload } from './utils';
+export { urlSafeBase64Encode, urlSafeBase64Decode } from './base64';
+export { deleteUploadedChunks, getUploadUrl } from './api';
+export { imageMogr2, watermark, imageInfo, exif, pipeline } from './image';
+export { region } from './config';
+export { upload, compressImage };
+//# sourceMappingURL=index.js.map

+ 1 - 0
qiniu-js/esm/index.js.map

@@ -0,0 +1 @@
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,mBAAqE,MAAM,UAAU,CAAA;AAC5F,OAAO,EAAE,UAAU,EAAa,MAAM,cAAc,CAAA;AAGpD,OAAO,aAAa,MAAM,YAAY,CAAA;AACtC,OAAO,MAAM,MAAM,UAAU,CAAA;AAE7B;;;;;;;GAOG;AACH,SAAS,MAAM,CACb,IAAU,EACV,GAA8B,EAC9B,KAAa,EACb,QAAyB,EACzB,MAAwB;IAExB,IAAM,OAAO,GAAkB;QAC7B,IAAI,MAAA;QACJ,GAAG,KAAA;QACH,KAAK,OAAA;QACL,QAAQ,UAAA;QACR,MAAM,QAAA;KACP,CAAA;IAED,oBAAoB;IACpB,IAAM,MAAM,GAAG,IAAI,MAAM,CAAC,KAAK,EAAE,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,uBAAuB,EAAE,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,aAAa,CAAC,CAAA;IACxF,OAAO,IAAI,UAAU,CAAC,UAAC,QAAoE;QACzF,IAAM,OAAO,GAAG,mBAAmB,CAAC,OAAO,EAAE;YAC3C,MAAM,EAAE,UAAC,IAAoB,IAAK,OAAA,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAnB,CAAmB;YACrD,OAAO,EAAE,UAAC,GAAgB,IAAK,OAAA,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,EAAnB,CAAmB;YAClD,UAAU,EAAE,UAAC,GAAQ,IAAK,OAAA,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAtB,CAAsB;SACjD,EAAE,MAAM,CAAC,CAAA;QACV,OAAO,CAAC,OAAO,EAAE,CAAA;QACjB,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACnC,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACzB,MAAM,SAAS,CAAA;AAEhB,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AAInE,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AAC1D,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAC1E,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEjC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,CAAA"}

+ 31 - 0
qiniu-js/esm/logger/index.d.ts

@@ -0,0 +1,31 @@
+import { V3LogInfo } from './report-v3';
+export declare type LogLevel = 'INFO' | 'WARN' | 'ERROR' | 'OFF';
+export default class Logger {
+    private token;
+    private disableReport;
+    private level;
+    private static id;
+    private id;
+    constructor(token: string, disableReport?: boolean, level?: LogLevel);
+    /**
+     * @param  {V3LogInfo} data 上报的数据。
+     * @param  {boolean} retry 重试次数,可选,默认为 3。
+     * @description 向服务端上报统计信息。
+     */
+    report(data: V3LogInfo, retry?: number): void;
+    /**
+     * @param  {unknown[]} ...args
+     * @description 输出 info 级别的调试信息。
+     */
+    info(...args: unknown[]): void;
+    /**
+     * @param  {unknown[]} ...args
+     * @description 输出 warn 级别的调试信息。
+     */
+    warn(...args: unknown[]): void;
+    /**
+     * @param  {unknown[]} ...args
+     * @description 输出 error 级别的调试信息。
+     */
+    error(...args: unknown[]): void;
+}

+ 94 - 0
qiniu-js/esm/logger/index.js

@@ -0,0 +1,94 @@
+var __read = (this && this.__read) || function (o, n) {
+    var m = typeof Symbol === "function" && o[Symbol.iterator];
+    if (!m) return o;
+    var i = m.call(o), r, ar = [], e;
+    try {
+        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
+    }
+    catch (error) { e = { error: error }; }
+    finally {
+        try {
+            if (r && !r.done && (m = i["return"])) m.call(i);
+        }
+        finally { if (e) throw e.error; }
+    }
+    return ar;
+};
+var __spread = (this && this.__spread) || function () {
+    for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
+    return ar;
+};
+import { reportV3 } from './report-v3';
+var Logger = /** @class */ (function () {
+    function Logger(token, disableReport, level) {
+        if (disableReport === void 0) { disableReport = true; }
+        if (level === void 0) { level = 'OFF'; }
+        this.token = token;
+        this.disableReport = disableReport;
+        this.level = level;
+        // 为每个类分配一个 id
+        // 用以区分不同的上传任务
+        this.id = ++Logger.id;
+    }
+    /**
+     * @param  {V3LogInfo} data 上报的数据。
+     * @param  {boolean} retry 重试次数,可选,默认为 3。
+     * @description 向服务端上报统计信息。
+     */
+    Logger.prototype.report = function (data, retry) {
+        if (this.disableReport)
+            return;
+        try {
+            reportV3(this.token, data, retry);
+        }
+        catch (error) {
+            console.warn(error);
+        }
+    };
+    /**
+     * @param  {unknown[]} ...args
+     * @description 输出 info 级别的调试信息。
+     */
+    Logger.prototype.info = function () {
+        var args = [];
+        for (var _i = 0; _i < arguments.length; _i++) {
+            args[_i] = arguments[_i];
+        }
+        var allowLevel = ['INFO'];
+        if (allowLevel.includes(this.level)) {
+            console.log.apply(console, __spread(["Qiniu-JS-SDK [INFO][" + this.id + "]: "], args));
+        }
+    };
+    /**
+     * @param  {unknown[]} ...args
+     * @description 输出 warn 级别的调试信息。
+     */
+    Logger.prototype.warn = function () {
+        var args = [];
+        for (var _i = 0; _i < arguments.length; _i++) {
+            args[_i] = arguments[_i];
+        }
+        var allowLevel = ['INFO', 'WARN'];
+        if (allowLevel.includes(this.level)) {
+            console.warn.apply(console, __spread(["Qiniu-JS-SDK [WARN][" + this.id + "]: "], args));
+        }
+    };
+    /**
+     * @param  {unknown[]} ...args
+     * @description 输出 error 级别的调试信息。
+     */
+    Logger.prototype.error = function () {
+        var args = [];
+        for (var _i = 0; _i < arguments.length; _i++) {
+            args[_i] = arguments[_i];
+        }
+        var allowLevel = ['INFO', 'WARN', 'ERROR'];
+        if (allowLevel.includes(this.level)) {
+            console.error.apply(console, __spread(["Qiniu-JS-SDK [ERROR][" + this.id + "]: "], args));
+        }
+    };
+    Logger.id = 0;
+    return Logger;
+}());
+export default Logger;
+//# sourceMappingURL=index.js.map

+ 1 - 0
qiniu-js/esm/logger/index.js.map

@@ -0,0 +1 @@
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/logger/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,OAAO,EAAE,QAAQ,EAAa,MAAM,aAAa,CAAA;AAIjD;IAOE,gBACU,KAAa,EACb,aAAoB,EACpB,KAAuB;QADvB,8BAAA,EAAA,oBAAoB;QACpB,sBAAA,EAAA,aAAuB;QAFvB,UAAK,GAAL,KAAK,CAAQ;QACb,kBAAa,GAAb,aAAa,CAAO;QACpB,UAAK,GAAL,KAAK,CAAkB;QAPjC,cAAc;QACd,cAAc;QACN,OAAE,GAAG,EAAE,MAAM,CAAC,EAAE,CAAA;IAMpB,CAAC;IAEL;;;;OAIG;IACH,uBAAM,GAAN,UAAO,IAAe,EAAE,KAAc;QACpC,IAAI,IAAI,CAAC,aAAa;YAAE,OAAM;QAC9B,IAAI;YAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;SAAE;QACzC,OAAO,KAAK,EAAE;YAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;SAAE;IACvC,CAAC;IAED;;;OAGG;IACH,qBAAI,GAAJ;QAAK,cAAkB;aAAlB,UAAkB,EAAlB,qBAAkB,EAAlB,IAAkB;YAAlB,yBAAkB;;QACrB,IAAM,UAAU,GAAe,CAAC,MAAM,CAAC,CAAA;QACvC,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;YACnC,OAAO,CAAC,GAAG,OAAX,OAAO,YAAK,yBAAuB,IAAI,CAAC,EAAE,QAAK,GAAK,IAAI,GAAC;SAC1D;IACH,CAAC;IAED;;;OAGG;IACH,qBAAI,GAAJ;QAAK,cAAkB;aAAlB,UAAkB,EAAlB,qBAAkB,EAAlB,IAAkB;YAAlB,yBAAkB;;QACrB,IAAM,UAAU,GAAe,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC/C,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;YACnC,OAAO,CAAC,IAAI,OAAZ,OAAO,YAAM,yBAAuB,IAAI,CAAC,EAAE,QAAK,GAAK,IAAI,GAAC;SAC3D;IACH,CAAC;IAED;;;OAGG;IACH,sBAAK,GAAL;QAAM,cAAkB;aAAlB,UAAkB,EAAlB,qBAAkB,EAAlB,IAAkB;YAAlB,yBAAkB;;QACtB,IAAM,UAAU,GAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;QACxD,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;YACnC,OAAO,CAAC,KAAK,OAAb,OAAO,YAAO,0BAAwB,IAAI,CAAC,EAAE,QAAK,GAAK,IAAI,GAAC;SAC7D;IACH,CAAC;IAtDc,SAAE,GAAW,CAAC,CAAA;IAuD/B,aAAC;CAAA,AAxDD,IAwDC;eAxDoB,MAAM"}

+ 1 - 0
qiniu-js/esm/logger/index.test.d.ts

@@ -0,0 +1 @@
+export {};

+ 124 - 0
qiniu-js/esm/logger/index.test.js

@@ -0,0 +1,124 @@
+var __read = (this && this.__read) || function (o, n) {
+    var m = typeof Symbol === "function" && o[Symbol.iterator];
+    if (!m) return o;
+    var i = m.call(o), r, ar = [], e;
+    try {
+        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
+    }
+    catch (error) { e = { error: error }; }
+    finally {
+        try {
+            if (r && !r.done && (m = i["return"])) m.call(i);
+        }
+        finally { if (e) throw e.error; }
+    }
+    return ar;
+};
+var __spread = (this && this.__spread) || function () {
+    for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
+    return ar;
+};
+import Logger from './index';
+var isCallReport = false;
+jest.mock('./report-v3', function () { return ({
+    reportV3: function () {
+        isCallReport = true;
+    }
+}); });
+var originalLog = console.log;
+var originalWarn = console.warn;
+var originalError = console.error;
+var logMessage = [];
+var warnMessage = [];
+var errorMessage = [];
+beforeAll(function () {
+    console.log = jest.fn(function () {
+        var args = [];
+        for (var _i = 0; _i < arguments.length; _i++) {
+            args[_i] = arguments[_i];
+        }
+        return logMessage.push.apply(logMessage, __spread(args));
+    });
+    console.warn = jest.fn(function () {
+        var args = [];
+        for (var _i = 0; _i < arguments.length; _i++) {
+            args[_i] = arguments[_i];
+        }
+        return warnMessage.push.apply(warnMessage, __spread(args));
+    });
+    console.error = jest.fn(function () {
+        var args = [];
+        for (var _i = 0; _i < arguments.length; _i++) {
+            args[_i] = arguments[_i];
+        }
+        return errorMessage.push.apply(errorMessage, __spread(args));
+    });
+});
+afterAll(function () {
+    console.log = originalLog;
+    console.warn = originalWarn;
+    console.error = originalError;
+});
+describe('test logger', function () {
+    test('test level', function () {
+        var infoLogger = new Logger('', true, 'INFO');
+        infoLogger.info('test1');
+        expect(logMessage).toStrictEqual(["Qiniu-JS-SDK [INFO][1]: ", 'test1']);
+        infoLogger.warn('test2');
+        expect(warnMessage).toStrictEqual(['Qiniu-JS-SDK [WARN][1]: ', 'test2']);
+        infoLogger.error('test3');
+        expect(errorMessage).toStrictEqual(['Qiniu-JS-SDK [ERROR][1]: ', 'test3']);
+        // 清空消息
+        logMessage.splice(0, logMessage.length);
+        warnMessage.splice(0, warnMessage.length);
+        errorMessage.splice(0, errorMessage.length);
+        var warnLogger = new Logger('', true, 'WARN');
+        warnLogger.info('test1');
+        expect(logMessage).toStrictEqual([]);
+        warnLogger.warn('test2');
+        expect(warnMessage).toStrictEqual(['Qiniu-JS-SDK [WARN][2]: ', 'test2']);
+        warnLogger.error('test3');
+        expect(errorMessage).toStrictEqual(['Qiniu-JS-SDK [ERROR][2]: ', 'test3']);
+        // 清空消息
+        logMessage.splice(0, logMessage.length);
+        warnMessage.splice(0, warnMessage.length);
+        errorMessage.splice(0, errorMessage.length);
+        var errorLogger = new Logger('', true, 'ERROR');
+        errorLogger.info('test1');
+        expect(logMessage).toStrictEqual([]);
+        errorLogger.warn('test2');
+        expect(warnMessage).toStrictEqual([]);
+        errorLogger.error('test3');
+        expect(errorMessage).toStrictEqual(['Qiniu-JS-SDK [ERROR][3]: ', 'test3']);
+        // 清空消息
+        logMessage.splice(0, logMessage.length);
+        warnMessage.splice(0, warnMessage.length);
+        errorMessage.splice(0, errorMessage.length);
+        var offLogger = new Logger('', true, 'OFF');
+        offLogger.info('test1');
+        expect(logMessage).toStrictEqual([]);
+        offLogger.warn('test2');
+        expect(warnMessage).toStrictEqual([]);
+        offLogger.error('test3');
+        expect(errorMessage).toStrictEqual([]);
+    });
+    test('test unique id', function () {
+        // @ts-ignore
+        var startId = Logger.id;
+        new Logger('', true, 'OFF');
+        new Logger('', true, 'OFF');
+        var last = new Logger('', true, 'OFF');
+        // @ts-ignore
+        expect(last.id).toStrictEqual(startId + 3);
+    });
+    test('test report', function () {
+        var logger1 = new Logger('', false, 'OFF');
+        logger1.report(null);
+        expect(isCallReport).toBeTruthy();
+        isCallReport = false;
+        var logger2 = new Logger('', true, 'OFF');
+        logger2.report(null);
+        expect(isCallReport).toBeFalsy();
+    });
+});
+//# sourceMappingURL=index.test.js.map

Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
qiniu-js/esm/logger/index.test.js.map


+ 19 - 0
qiniu-js/esm/logger/report-v3.d.ts

@@ -0,0 +1,19 @@
+export interface V3LogInfo {
+    code: number;
+    reqId: string;
+    host: string;
+    remoteIp: string;
+    port: string;
+    duration: number;
+    time: number;
+    bytesSent: number;
+    upType: 'jssdk-h5';
+    size: number;
+}
+/**
+ * @param  {string} token 上传使用的 token
+ * @param  {V3LogInfo} data 上报的统计数据
+ * @param  {number} retry 重试的次数,默认值 3
+ * @description v3 版本的日志上传接口,参考文档 https://github.com/qbox/product/blob/master/kodo/uplog.md#%E7%89%88%E6%9C%AC-3。
+ */
+export declare function reportV3(token: string, data: V3LogInfo, retry?: number): void;

+ 34 - 0
qiniu-js/esm/logger/report-v3.js

@@ -0,0 +1,34 @@
+import { createXHR, getAuthHeaders } from '../utils';
+/**
+ * @param  {string} token 上传使用的 token
+ * @param  {V3LogInfo} data 上报的统计数据
+ * @param  {number} retry 重试的次数,默认值 3
+ * @description v3 版本的日志上传接口,参考文档 https://github.com/qbox/product/blob/master/kodo/uplog.md#%E7%89%88%E6%9C%AC-3。
+ */
+export function reportV3(token, data, retry) {
+    if (retry === void 0) { retry = 3; }
+    var xhr = createXHR();
+    xhr.open('POST', 'https://uplog.qbox.me/log/3');
+    xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+    xhr.setRequestHeader('Authorization', getAuthHeaders(token).Authorization);
+    xhr.onreadystatechange = function () {
+        if (xhr.readyState === 4 && xhr.status !== 200 && retry > 0) {
+            reportV3(token, data, retry - 1);
+        }
+    };
+    // 顺序参考:https://github.com/qbox/product/blob/master/kodo/uplog.md#%E7%89%88%E6%9C%AC-3
+    var stringifyData = [
+        data.code || '',
+        data.reqId || '',
+        data.host || '',
+        data.remoteIp || '',
+        data.port || '',
+        data.duration || '',
+        data.time || '',
+        data.bytesSent || '',
+        data.upType || '',
+        data.size || ''
+    ].join(',');
+    xhr.send(stringifyData);
+}
+//# sourceMappingURL=report-v3.js.map

+ 1 - 0
qiniu-js/esm/logger/report-v3.js.map

@@ -0,0 +1 @@
+{"version":3,"file":"report-v3.js","sourceRoot":"","sources":["../../src/logger/report-v3.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAepD;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAa,EAAE,IAAe,EAAE,KAAS;IAAT,sBAAA,EAAA,SAAS;IAChE,IAAM,GAAG,GAAG,SAAS,EAAE,CAAA;IACvB,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,6BAA6B,CAAC,CAAA;IAC/C,GAAG,CAAC,gBAAgB,CAAC,cAAc,EAAE,mCAAmC,CAAC,CAAA;IACzE,GAAG,CAAC,gBAAgB,CAAC,eAAe,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,aAAa,CAAC,CAAA;IAC1E,GAAG,CAAC,kBAAkB,GAAG;QACvB,IAAI,GAAG,CAAC,UAAU,KAAK,CAAC,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,KAAK,GAAG,CAAC,EAAE;YAC3D,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAA;SACjC;IACH,CAAC,CAAA;IAED,sFAAsF;IACtF,IAAM,aAAa,GAAG;QACpB,IAAI,CAAC,IAAI,IAAI,EAAE;QACf,IAAI,CAAC,KAAK,IAAI,EAAE;QAChB,IAAI,CAAC,IAAI,IAAI,EAAE;QACf,IAAI,CAAC,QAAQ,IAAI,EAAE;QACnB,IAAI,CAAC,IAAI,IAAI,EAAE;QACf,IAAI,CAAC,QAAQ,IAAI,EAAE;QACnB,IAAI,CAAC,IAAI,IAAI,EAAE;QACf,IAAI,CAAC,SAAS,IAAI,EAAE;QACpB,IAAI,CAAC,MAAM,IAAI,EAAE;QACjB,IAAI,CAAC,IAAI,IAAI,EAAE;KAChB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAEX,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;AACzB,CAAC"}

+ 1 - 0
qiniu-js/esm/logger/report-v3.test.d.ts

@@ -0,0 +1 @@
+export {};

+ 115 - 0
qiniu-js/esm/logger/report-v3.test.js

@@ -0,0 +1,115 @@
+var __read = (this && this.__read) || function (o, n) {
+    var m = typeof Symbol === "function" && o[Symbol.iterator];
+    if (!m) return o;
+    var i = m.call(o), r, ar = [], e;
+    try {
+        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
+    }
+    catch (error) { e = { error: error }; }
+    finally {
+        try {
+            if (r && !r.done && (m = i["return"])) m.call(i);
+        }
+        finally { if (e) throw e.error; }
+    }
+    return ar;
+};
+var __spread = (this && this.__spread) || function () {
+    for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
+    return ar;
+};
+import { reportV3 } from './report-v3';
+var MockXHR = /** @class */ (function () {
+    function MockXHR() {
+    }
+    MockXHR.prototype.onreadystatechange = function () { };
+    MockXHR.prototype.clear = function () {
+        this.sendData = '';
+        this.openData = [];
+        this.headerData = [];
+        this.status = 0;
+        this.readyState = 0;
+    };
+    MockXHR.prototype.open = function () {
+        var args = [];
+        for (var _i = 0; _i < arguments.length; _i++) {
+            args[_i] = arguments[_i];
+        }
+        this.clear();
+        this.openCount += 1;
+        this.openData = args;
+    };
+    MockXHR.prototype.send = function (args) {
+        this.sendData = args;
+    };
+    MockXHR.prototype.setRequestHeader = function () {
+        var _a;
+        var args = [];
+        for (var _i = 0; _i < arguments.length; _i++) {
+            args[_i] = arguments[_i];
+        }
+        (_a = this.headerData).push.apply(_a, __spread(args));
+    };
+    MockXHR.prototype.changeStatusAndState = function (readyState, status) {
+        this.status = status;
+        this.readyState = readyState;
+        this.onreadystatechange();
+    };
+    return MockXHR;
+}());
+var mockXHR = new MockXHR();
+jest.mock('../utils', function () { return ({
+    createXHR: function () { return mockXHR; },
+    getAuthHeaders: function (t) { return t; }
+}); });
+describe('test report-v3', function () {
+    var testData = {
+        code: 200,
+        reqId: 'reqId',
+        host: 'host',
+        remoteIp: 'remoteIp',
+        port: 'port',
+        duration: 1,
+        time: 1,
+        bytesSent: 1,
+        upType: 'jssdk-h5',
+        size: 1
+    };
+    test('test stringify send Data', function () {
+        reportV3('token', testData, 3);
+        mockXHR.changeStatusAndState(0, 0);
+        expect(mockXHR.sendData).toBe([
+            testData.code || '',
+            testData.reqId || '',
+            testData.host || '',
+            testData.remoteIp || '',
+            testData.port || '',
+            testData.duration || '',
+            testData.time || '',
+            testData.bytesSent || '',
+            testData.upType || '',
+            testData.size || ''
+        ].join(','));
+    });
+    test('test retry', function () {
+        mockXHR.openCount = 0;
+        reportV3('token', testData);
+        for (var index = 1; index <= 10; index++) {
+            mockXHR.changeStatusAndState(4, 0);
+        }
+        expect(mockXHR.openCount).toBe(4);
+        mockXHR.openCount = 0;
+        reportV3('token', testData, 4);
+        for (var index = 1; index < 10; index++) {
+            mockXHR.changeStatusAndState(4, 0);
+        }
+        expect(mockXHR.openCount).toBe(5);
+        mockXHR.openCount = 0;
+        reportV3('token', testData, 0);
+        for (var index = 1; index < 10; index++) {
+            mockXHR.changeStatusAndState(4, 0);
+        }
+        expect(mockXHR.openCount).toBe(1);
+    });
+});
+//# sourceMappingURL=report-v3.test.js.map

Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
qiniu-js/esm/logger/report-v3.test.js.map


+ 71 - 0
qiniu-js/esm/observable.d.ts

@@ -0,0 +1,71 @@
+/** 消费者接口 */
+export interface IObserver<T, E, C> {
+    /** 用来接收 Observable 中的 next 类型通知 */
+    next: (value: T) => void;
+    /** 用来接收 Observable 中的 error 类型通知 */
+    error: (err: E) => void;
+    /** 用来接收 Observable 中的 complete 类型通知 */
+    complete: (res: C) => void;
+}
+export interface NextObserver<T, E, C> {
+    next: (value: T) => void;
+    error?: (err: E) => void;
+    complete?: (res: C) => void;
+}
+export interface ErrorObserver<T, E, C> {
+    next?: (value: T) => void;
+    error: (err: E) => void;
+    complete?: (res: C) => void;
+}
+export interface CompletionObserver<T, E, C> {
+    next?: (value: T) => void;
+    error?: (err: E) => void;
+    complete: (res: C) => void;
+}
+export declare type PartialObserver<T, E, C> = NextObserver<T, E, C> | ErrorObserver<T, E, C> | CompletionObserver<T, E, C>;
+export interface IUnsubscribable {
+    /** 取消 observer 的订阅 */
+    unsubscribe(): void;
+}
+/** Subscription 的接口 */
+export interface ISubscriptionLike extends IUnsubscribable {
+    readonly closed: boolean;
+}
+export declare type TeardownLogic = () => void;
+export interface ISubscribable<T, E, C> {
+    subscribe(observer?: PartialObserver<T, E, C> | ((value: T) => void), error?: (error: any) => void, complete?: () => void): IUnsubscribable;
+}
+/** 表示可清理的资源,比如 Observable 的执行 */
+declare class Subscription implements ISubscriptionLike {
+    /** 用来标示该 Subscription 是否被取消订阅的标示位 */
+    closed: boolean;
+    /** 清理 subscription 持有的资源 */
+    private _unsubscribe;
+    /** 取消 observer 的订阅 */
+    unsubscribe(): void;
+    /** 添加一个 tear down 在该 Subscription 的 unsubscribe() 期间调用 */
+    add(teardown: TeardownLogic): void;
+}
+/**
+ * 实现 Observer 接口并且继承 Subscription 类,Observer 是消费 Observable 值的公有 API
+ * 所有 Observers 都转化成了 Subscriber,以便提供类似 Subscription 的能力,比如 unsubscribe
+*/
+export declare class Subscriber<T, E, C> extends Subscription implements IObserver<T, E, C> {
+    protected isStopped: boolean;
+    protected destination: Partial<IObserver<T, E, C>>;
+    constructor(observerOrNext?: PartialObserver<T, E, C> | ((value: T) => void) | null, error?: ((err: E) => void) | null, complete?: ((res: C) => void) | null);
+    unsubscribe(): void;
+    next(value: T): void;
+    error(err: E): void;
+    complete(result: C): void;
+}
+/** 可观察对象,当前的上传事件的集合 */
+export declare class Observable<T, E, C> implements ISubscribable<T, E, C> {
+    private _subscribe;
+    constructor(_subscribe: (subscriber: Subscriber<T, E, C>) => TeardownLogic);
+    subscribe(observer: PartialObserver<T, E, C>): Subscription;
+    subscribe(next: null | undefined, error: null | undefined, complete: (res: C) => void): Subscription;
+    subscribe(next: null | undefined, error: (error: E) => void, complete?: (res: C) => void): Subscription;
+    subscribe(next: (value: T) => void, error: null | undefined, complete: (res: C) => void): Subscription;
+}
+export {};

+ 104 - 0
qiniu-js/esm/observable.js

@@ -0,0 +1,104 @@
+var __extends = (this && this.__extends) || (function () {
+    var extendStatics = function (d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+    return function (d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    };
+})();
+var __assign = (this && this.__assign) || function () {
+    __assign = Object.assign || function(t) {
+        for (var s, i = 1, n = arguments.length; i < n; i++) {
+            s = arguments[i];
+            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
+                t[p] = s[p];
+        }
+        return t;
+    };
+    return __assign.apply(this, arguments);
+};
+/** 表示可清理的资源,比如 Observable 的执行 */
+var Subscription = /** @class */ (function () {
+    function Subscription() {
+        /** 用来标示该 Subscription 是否被取消订阅的标示位 */
+        this.closed = false;
+    }
+    /** 取消 observer 的订阅 */
+    Subscription.prototype.unsubscribe = function () {
+        if (this.closed) {
+            return;
+        }
+        this.closed = true;
+        if (this._unsubscribe) {
+            this._unsubscribe();
+        }
+    };
+    /** 添加一个 tear down 在该 Subscription 的 unsubscribe() 期间调用 */
+    Subscription.prototype.add = function (teardown) {
+        this._unsubscribe = teardown;
+    };
+    return Subscription;
+}());
+/**
+ * 实现 Observer 接口并且继承 Subscription 类,Observer 是消费 Observable 值的公有 API
+ * 所有 Observers 都转化成了 Subscriber,以便提供类似 Subscription 的能力,比如 unsubscribe
+*/
+var Subscriber = /** @class */ (function (_super) {
+    __extends(Subscriber, _super);
+    function Subscriber(observerOrNext, error, complete) {
+        var _this = _super.call(this) || this;
+        _this.isStopped = false;
+        if (observerOrNext && typeof observerOrNext === 'object') {
+            _this.destination = observerOrNext;
+        }
+        else {
+            _this.destination = __assign(__assign(__assign({}, observerOrNext && { next: observerOrNext }), error && { error: error }), complete && { complete: complete });
+        }
+        return _this;
+    }
+    Subscriber.prototype.unsubscribe = function () {
+        if (this.closed) {
+            return;
+        }
+        this.isStopped = true;
+        _super.prototype.unsubscribe.call(this);
+    };
+    Subscriber.prototype.next = function (value) {
+        if (!this.isStopped && this.destination.next) {
+            this.destination.next(value);
+        }
+    };
+    Subscriber.prototype.error = function (err) {
+        if (!this.isStopped && this.destination.error) {
+            this.isStopped = true;
+            this.destination.error(err);
+        }
+    };
+    Subscriber.prototype.complete = function (result) {
+        if (!this.isStopped && this.destination.complete) {
+            this.isStopped = true;
+            this.destination.complete(result);
+        }
+    };
+    return Subscriber;
+}(Subscription));
+export { Subscriber };
+/** 可观察对象,当前的上传事件的集合 */
+var Observable = /** @class */ (function () {
+    function Observable(_subscribe) {
+        this._subscribe = _subscribe;
+    }
+    Observable.prototype.subscribe = function (observerOrNext, error, complete) {
+        var sink = new Subscriber(observerOrNext, error, complete);
+        sink.add(this._subscribe(sink));
+        return sink;
+    };
+    return Observable;
+}());
+export { Observable };
+//# sourceMappingURL=observable.js.map

+ 1 - 0
qiniu-js/esm/observable.js.map

@@ -0,0 +1 @@
+{"version":3,"file":"observable.js","sourceRoot":"","sources":["../src/observable.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAkDA,iCAAiC;AACjC;IAAA;QACE,qCAAqC;QAC9B,WAAM,GAAG,KAAK,CAAA;IAqBvB,CAAC;IAhBC,sBAAsB;IACtB,kCAAW,GAAX;QACE,IAAI,IAAI,CAAC,MAAM,EAAE;YACf,OAAM;SACP;QAED,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA;QAClB,IAAI,IAAI,CAAC,YAAY,EAAE;YACrB,IAAI,CAAC,YAAY,EAAE,CAAA;SACpB;IACH,CAAC;IAED,0DAA0D;IAC1D,0BAAG,GAAH,UAAI,QAAuB;QACzB,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAA;IAC9B,CAAC;IACH,mBAAC;AAAD,CAAC,AAvBD,IAuBC;AAED;;;EAGE;AACF;IAAyC,8BAAY;IAInD,oBACE,cAAuE,EACvE,KAAiC,EACjC,QAAoC;QAHtC,YAKE,iBAAO,SAWR;QAnBS,eAAS,GAAG,KAAK,CAAA;QAUzB,IAAI,cAAc,IAAI,OAAO,cAAc,KAAK,QAAQ,EAAE;YACxD,KAAI,CAAC,WAAW,GAAG,cAAc,CAAA;SAClC;aAAM;YACL,KAAI,CAAC,WAAW,kCACX,cAAc,IAAI,EAAE,IAAI,EAAE,cAAc,EAAE,GAC1C,KAAK,IAAI,EAAE,KAAK,OAAA,EAAE,GAClB,QAAQ,IAAI,EAAE,QAAQ,UAAA,EAAE,CAC5B,CAAA;SACF;;IACH,CAAC;IAED,gCAAW,GAAX;QACE,IAAI,IAAI,CAAC,MAAM,EAAE;YACf,OAAM;SACP;QAED,IAAI,CAAC,SAAS,GAAG,IAAI,CAAA;QACrB,iBAAM,WAAW,WAAE,CAAA;IACrB,CAAC;IAED,yBAAI,GAAJ,UAAK,KAAQ;QACX,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE;YAC5C,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;SAC7B;IACH,CAAC;IAED,0BAAK,GAAL,UAAM,GAAM;QACV,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE;YAC7C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAA;YACrB,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;SAC5B;IACH,CAAC;IAED,6BAAQ,GAAR,UAAS,MAAS;QAChB,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE;YAChD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAA;YACrB,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;SAClC;IACH,CAAC;IACH,iBAAC;AAAD,CAAC,AAlDD,CAAyC,YAAY,GAkDpD;;AAED,uBAAuB;AACvB;IAEE,oBAAoB,UAA8D;QAA9D,eAAU,GAAV,UAAU,CAAoD;IAAG,CAAC;IAMtF,8BAAS,GAAT,UACE,cAAuE,EACvE,KAAiC,EACjC,QAAoC;QAEpC,IAAM,IAAI,GAAG,IAAI,UAAU,CAAC,cAAc,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAA;QAC5D,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAA;QAC/B,OAAO,IAAI,CAAA;IACb,CAAC;IACH,iBAAC;AAAD,CAAC,AAjBD,IAiBC"}

+ 16 - 0
qiniu-js/esm/pool.d.ts

@@ -0,0 +1,16 @@
+export declare type RunTask<T> = (...args: T[]) => Promise<void>;
+export interface QueueContent<T> {
+    task: T;
+    resolve: () => void;
+    reject: (err?: any) => void;
+}
+export declare class Pool<T> {
+    private runTask;
+    private limit;
+    queue: Array<QueueContent<T>>;
+    processing: Array<QueueContent<T>>;
+    constructor(runTask: RunTask<T>, limit: number);
+    enqueue(task: T): Promise<void>;
+    run(item: QueueContent<T>): void;
+    check(): void;
+}

+ 40 - 0
qiniu-js/esm/pool.js

@@ -0,0 +1,40 @@
+var Pool = /** @class */ (function () {
+    function Pool(runTask, limit) {
+        this.runTask = runTask;
+        this.limit = limit;
+        this.queue = [];
+        this.processing = [];
+    }
+    Pool.prototype.enqueue = function (task) {
+        var _this = this;
+        return new Promise(function (resolve, reject) {
+            _this.queue.push({
+                task: task,
+                resolve: resolve,
+                reject: reject
+            });
+            _this.check();
+        });
+    };
+    Pool.prototype.run = function (item) {
+        var _this = this;
+        this.queue = this.queue.filter(function (v) { return v !== item; });
+        this.processing.push(item);
+        this.runTask(item.task).then(function () {
+            _this.processing = _this.processing.filter(function (v) { return v !== item; });
+            item.resolve();
+            _this.check();
+        }, function (err) { return item.reject(err); });
+    };
+    Pool.prototype.check = function () {
+        var _this = this;
+        var processingNum = this.processing.length;
+        var availableNum = this.limit - processingNum;
+        this.queue.slice(0, availableNum).forEach(function (item) {
+            _this.run(item);
+        });
+    };
+    return Pool;
+}());
+export { Pool };
+//# sourceMappingURL=pool.js.map

+ 1 - 0
qiniu-js/esm/pool.js.map

@@ -0,0 +1 @@
+{"version":3,"file":"pool.js","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAQA;IAIE,cAAoB,OAAmB,EAAU,KAAa;QAA1C,YAAO,GAAP,OAAO,CAAY;QAAU,UAAK,GAAL,KAAK,CAAQ;QAH9D,UAAK,GAA2B,EAAE,CAAA;QAClC,eAAU,GAA2B,EAAE,CAAA;IAE0B,CAAC;IAElE,sBAAO,GAAP,UAAQ,IAAO;QAAf,iBASC;QARC,OAAO,IAAI,OAAO,CAAO,UAAC,OAAO,EAAE,MAAM;YACvC,KAAI,CAAC,KAAK,CAAC,IAAI,CAAC;gBACd,IAAI,MAAA;gBACJ,OAAO,SAAA;gBACP,MAAM,QAAA;aACP,CAAC,CAAA;YACF,KAAI,CAAC,KAAK,EAAE,CAAA;QACd,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,kBAAG,GAAH,UAAI,IAAqB;QAAzB,iBAWC;QAVC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,UAAA,CAAC,IAAI,OAAA,CAAC,KAAK,IAAI,EAAV,CAAU,CAAC,CAAA;QAC/C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC1B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAC1B;YACE,KAAI,CAAC,UAAU,GAAG,KAAI,CAAC,UAAU,CAAC,MAAM,CAAC,UAAA,CAAC,IAAI,OAAA,CAAC,KAAK,IAAI,EAAV,CAAU,CAAC,CAAA;YACzD,IAAI,CAAC,OAAO,EAAE,CAAA;YACd,KAAI,CAAC,KAAK,EAAE,CAAA;QACd,CAAC,EACD,UAAA,GAAG,IAAI,OAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAhB,CAAgB,CACxB,CAAA;IACH,CAAC;IAED,oBAAK,GAAL;QAAA,iBAMC;QALC,IAAM,aAAa,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAA;QAC5C,IAAM,YAAY,GAAG,IAAI,CAAC,KAAK,GAAG,aAAa,CAAA;QAC/C,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,OAAO,CAAC,UAAA,IAAI;YAC5C,KAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAChB,CAAC,CAAC,CAAA;IACJ,CAAC;IACH,WAAC;AAAD,CAAC,AArCD,IAqCC"}

+ 111 - 0
qiniu-js/esm/upload/base.d.ts

@@ -0,0 +1,111 @@
+import { region } from '../config';
+import Logger, { LogLevel } from '../logger';
+import * as utils from '../utils';
+export declare const DEFAULT_CHUNK_SIZE = 4;
+/** 上传文件的资源信息配置 */
+export interface Extra {
+    /** 文件原文件名 */
+    fname: string;
+    /** 用来放置自定义变量 */
+    customVars?: {
+        [key: string]: string;
+    };
+    /** 自定义元信息 */
+    metadata?: {
+        [key: string]: string;
+    };
+    /** 文件类型设置 */
+    mimeType?: string;
+}
+/** 上传任务的配置信息 */
+export interface Config {
+    /** 是否开启 cdn 加速 */
+    useCdnDomain: boolean;
+    /** 是否对分片进行 md5校验 */
+    checkByMD5: boolean;
+    /** 强制直传 */
+    forceDirect: boolean;
+    /** 上传失败后重试次数 */
+    retryCount: number;
+    /** 自定义上传域名 */
+    uphost: string;
+    /** 自定义分片上传并发请求量 */
+    concurrentRequestLimit: number;
+    /** 分片大小,单位为 MB */
+    chunkSize: number;
+    /** 上传域名协议 */
+    upprotocol: 'http:' | 'https:';
+    /** 上传区域 */
+    region?: typeof region[keyof typeof region];
+    /** 是否禁止统计日志上报 */
+    disableStatisticsReport: boolean;
+    /** 设置调试日志输出模式,默认 `OFF`,不输出任何日志 */
+    debugLogLevel?: LogLevel;
+}
+export interface UploadOptions {
+    file: File;
+    key: string | null | undefined;
+    token: string;
+    putExtra?: Partial<Extra>;
+    config?: Partial<Config>;
+}
+export interface UploadInfo {
+    id: string;
+    url: string;
+}
+/** 传递给外部的上传进度信息 */
+export interface UploadProgress {
+    total: ProgressCompose;
+    uploadInfo?: UploadInfo;
+    chunks?: ProgressCompose[];
+}
+export interface UploadHandler {
+    onData: (data: UploadProgress) => void;
+    onError: (err: utils.CustomError) => void;
+    onComplete: (res: any) => void;
+}
+export interface Progress {
+    loaded: number;
+    total: number;
+}
+export interface ProgressCompose {
+    loaded: number;
+    size: number;
+    percent: number;
+}
+export declare type XHRHandler = (xhr: XMLHttpRequest) => void;
+export default abstract class Base {
+    protected logger: Logger;
+    protected config: Config;
+    protected putExtra: Extra;
+    protected xhrList: XMLHttpRequest[];
+    protected file: File;
+    protected key: string | null | undefined;
+    protected aborted: boolean;
+    protected retryCount: number;
+    protected token: string;
+    protected uploadUrl: string;
+    protected bucket: string;
+    protected uploadAt: number;
+    protected progress: UploadProgress;
+    protected onData: (data: UploadProgress) => void;
+    protected onError: (err: utils.CustomError) => void;
+    protected onComplete: (res: any) => void;
+    protected abstract run(): utils.Response<any>;
+    constructor(options: UploadOptions, handlers: UploadHandler, logger: Logger);
+    private handleError;
+    /**
+     * @returns Promise 返回结果与上传最终状态无关,状态信息请通过 [Subscriber] 获取。
+     * @description 上传文件,状态信息请通过 [Subscriber] 获取。
+     */
+    putFile(): Promise<void>;
+    private clear;
+    stop(): void;
+    addXhr(xhr: XMLHttpRequest): void;
+    private sendLog;
+    getProgressInfoItem(loaded: number, size: number): {
+        loaded: number;
+        size: number;
+        percent: number;
+    };
+}

+ 194 - 0
qiniu-js/esm/upload/base.js

@@ -0,0 +1,194 @@
+var __assign = (this && this.__assign) || function () {
+    __assign = Object.assign || function(t) {
+        for (var s, i = 1, n = arguments.length; i < n; i++) {
+            s = arguments[i];
+            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
+                t[p] = s[p];
+        }
+        return t;
+    };
+    return __assign.apply(this, arguments);
+};
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+var __generator = (this && this.__generator) || function (thisArg, body) {
+    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
+    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
+    function verb(n) { return function (v) { return step([n, v]); }; }
+    function step(op) {
+        if (f) throw new TypeError("Generator is already executing.");
+        while (_) try {
+            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
+            if (y = 0, t) op = [op[0] & 2, t.value];
+            switch (op[0]) {
+                case 0: case 1: t = op; break;
+                case 4: _.label++; return { value: op[1], done: false };
+                case 5: _.label++; y = op[1]; op = [0]; continue;
+                case 7: op = _.ops.pop(); _.trys.pop(); continue;
+                default:
+                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
+                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
+                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
+                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
+                    if (t[2]) _.ops.pop();
+                    _.trys.pop(); continue;
+            }
+            op = body.call(thisArg, _);
+        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
+        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
+    }
+};
+import { getUploadUrl } from '../api';
+import * as utils from '../utils';
+export var DEFAULT_CHUNK_SIZE = 4; // 单位 MB
+var GB = Math.pow(1024, 3);
+var Base = /** @class */ (function () {
+    function Base(options, handlers, logger) {
+        this.logger = logger;
+        this.xhrList = [];
+        this.aborted = false;
+        this.retryCount = 0;
+        this.config = __assign({ useCdnDomain: true, disableStatisticsReport: false, retryCount: 3, checkByMD5: false, uphost: '', upprotocol: 'https:', forceDirect: false, chunkSize: DEFAULT_CHUNK_SIZE, concurrentRequestLimit: 3 }, options.config);
+        logger.info('config inited.', this.config);
+        this.putExtra = __assign({ fname: '' }, options.putExtra);
+        logger.info('putExtra inited.', this.putExtra);
+        this.file = options.file;
+        this.key = options.key;
+        this.token = options.token;
+        this.onData = handlers.onData;
+        this.onError = handlers.onError;
+        this.onComplete = handlers.onComplete;
+        try {
+            this.bucket = utils.getPutPolicy(this.token).bucket;
+        }
+        catch (e) {
+            logger.error('get bucket from token failed.', e);
+            this.onError(e);
+        }
+    }
+    Base.prototype.handleError = function (message) {
+        var err = new Error(message);
+        this.logger.error(message);
+        this.onError(err);
+    };
+    /**
+     * @returns Promise 返回结果与上传最终状态无关,状态信息请通过 [Subscriber] 获取。
+     * @description 上传文件,状态信息请通过 [Subscriber] 获取。
+     */
+    Base.prototype.putFile = function () {
+        return __awaiter(this, void 0, void 0, function () {
+            var _a, result, err_1, reqId, code, needRetry, notReachRetryCount;
+            return __generator(this, function (_b) {
+                switch (_b.label) {
+                    case 0:
+                        this.aborted = false;
+                        if (!this.putExtra.fname) {
+                            this.logger.info('use file.name as fname.');
+                            this.putExtra.fname = this.file.name;
+                        }
+                        if (this.file.size > 10000 * GB) {
+                            this.handleError('file size exceed maximum value 10000G.');
+                            return [2 /*return*/];
+                        }
+                        if (this.putExtra.customVars) {
+                            if (!utils.isCustomVarsValid(this.putExtra.customVars)) {
+                                this.handleError('customVars key should start width x:.');
+                                return [2 /*return*/];
+                            }
+                        }
+                        if (this.putExtra.metadata) {
+                            if (!utils.isMetaDataValid(this.putExtra.metadata)) {
+                                this.handleError('metadata key should start with x-qn-meta-.');
+                                return [2 /*return*/];
+                            }
+                        }
+                        _b.label = 1;
+                    case 1:
+                        _b.trys.push([1, 4, , 5]);
+                        _a = this;
+                        return [4 /*yield*/, getUploadUrl(this.config, this.token)];
+                    case 2:
+                        _a.uploadUrl = _b.sent();
+                        this.logger.info('get uploadUrl from api.', this.uploadUrl);
+                        this.uploadAt = new Date().getTime();
+                        return [4 /*yield*/, this.run()];
+                    case 3:
+                        result = _b.sent();
+                        this.onComplete(result.data);
+                        this.sendLog(result.reqId, 200);
+                        return [2 /*return*/];
+                    case 4:
+                        err_1 = _b.sent();
+                        this.logger.error(err_1);
+                        this.clear();
+                        if (err_1.isRequestError) {
+                            reqId = this.aborted ? '' : err_1.reqId;
+                            code = this.aborted ? -2 : err_1.code;
+                            this.sendLog(reqId, code);
+                        }
+                        needRetry = err_1.isRequestError && err_1.code === 0 && !this.aborted;
+                        notReachRetryCount = ++this.retryCount <= this.config.retryCount;
+                        // 以下条件满足其中之一则会进行重新上传:
+                        // 1. 满足 needRetry 的条件且 retryCount 不为 0
+                        // 2. uploadId 无效时在 resume 里会清除本地数据,并且这里触发重新上传
+                        if (needRetry && notReachRetryCount || err_1.code === 612) {
+                            this.logger.warn("error auto retry: " + this.retryCount + "/" + this.config.retryCount + ".");
+                            this.putFile();
+                            return [2 /*return*/];
+                        }
+                        this.onError(err_1);
+                        return [3 /*break*/, 5];
+                    case 5: return [2 /*return*/];
+                }
+            });
+        });
+    };
+    Base.prototype.clear = function () {
+        this.logger.info('start cleaning all xhr.');
+        this.xhrList.forEach(function (xhr) {
+            xhr.onreadystatechange = null;
+            xhr.abort();
+        });
+        this.logger.info('cleanup completed.');
+        this.xhrList = [];
+    };
+    Base.prototype.stop = function () {
+        this.logger.info('stop.');
+        this.clear();
+        this.aborted = true;
+    };
+    Base.prototype.addXhr = function (xhr) {
+        this.xhrList.push(xhr);
+    };
+    Base.prototype.sendLog = function (reqId, code) {
+        this.logger.report({
+            code: code,
+            reqId: reqId,
+            host: utils.getDomainFromUrl(this.uploadUrl),
+            remoteIp: '',
+            port: utils.getPortFromUrl(this.uploadUrl),
+            duration: (new Date().getTime() - this.uploadAt) / 1000,
+            time: Math.floor(this.uploadAt / 1000),
+            bytesSent: this.progress ? this.progress.total.loaded : 0,
+            upType: 'jssdk-h5',
+            size: this.file.size
+        });
+    };
+    Base.prototype.getProgressInfoItem = function (loaded, size) {
+        return {
+            loaded: loaded,
+            size: size,
+            percent: loaded / size * 100
+        };
+    };
+    return Base;
+}());
+export default Base;
+//# sourceMappingURL=base.js.map

Неке датотеке нису приказане због велике количине промена