Skip to content

Commit c6579d0

Browse files
committed
feat(File): Construct AV.FIle with blob and upload directly to Qiniu
1 parent 406d2b0 commit c6579d0

File tree

2 files changed

+190
-3
lines changed

2 files changed

+190
-3
lines changed

lib/file.js

Lines changed: 98 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,9 @@
329329
this._metaData.size = data.length;
330330
} else if (data && data.base64) {
331331
this._source = AV.Promise.as(data.base64, guessedType);
332+
} else if (data && data.blob) {
333+
this._source = AV.Promise.as(data.blob, guessedType);
334+
this._isBlob = true;
332335
} else if (typeof(File) !== "undefined" && data instanceof File) {
333336
this._source = readAsync(data, type);
334337
} else if(AV._isNode && Buffer.isBuffer(data)) {
@@ -499,12 +502,26 @@
499502
return request._thenRunCallbacks(options);
500503
},
501504

505+
/**
506+
* @callback UploadProgressCallback
507+
* @param {XMLHttpRequestProgressEvent} event - The progress event with 'loaded' and 'total' attributes
508+
*/
502509
/**
503510
* Saves the file to the AV cloud.
511+
* @param {Object} saveOptions
512+
* @param {UploadProgressCallback} [saveOptions.onProgress]
504513
* @param {Object} options A Backbone-style options object.
505514
* @return {AV.Promise} Promise that is resolved when the save finishes.
506515
*/
507-
save: function(options) {
516+
save: function() {
517+
var options = null;
518+
var saveOptions = {};
519+
if(arguments.length === 1) {
520+
options = arguments[0];
521+
} else if(arguments.length === 2) {
522+
saveOptions = arguments[0];
523+
options = arguments[1];
524+
}
508525
var self = this;
509526
if (!self._previousSave) {
510527
if(self._source){
@@ -524,7 +541,7 @@
524541
ACL: self._acl,
525542
name:self._name,
526543
mime_type: type,
527-
metaData: self._metaData,
544+
metaData: self._metaData
528545
};
529546
if(type && self._metaData.mime_type == null)
530547
self._metaData.mime_type = type;
@@ -553,6 +570,84 @@
553570
self.destroy();
554571
}
555572
});
573+
return promise;
574+
});
575+
} else if (this._isBlob) {
576+
577+
self._previousSave = self._source.then(function(blob, type) {
578+
//Create 16-bits uuid as qiniu key.
579+
var hexOctet = function() {
580+
return Math.floor((1+Math.random())*0x10000).toString(16).substring(1);
581+
};
582+
var nameParts = self._name.split('.');
583+
var key = hexOctet() + hexOctet() + hexOctet() + hexOctet() + hexOctet()
584+
+ '.' + nameParts[nameParts.length - 1];
585+
var data = {
586+
key: key,
587+
ACL: self._acl,
588+
name:self._name,
589+
mime_type: type,
590+
metaData: self._metaData
591+
};
592+
if(type && self._metaData.mime_type == null)
593+
self._metaData.mime_type = type;
594+
self._qiniu_key = key;
595+
self._blob = blob;
596+
return AV._request("qiniu", null, null, 'POST', data);
597+
}).then(function(response) {
598+
self._url = response.url;
599+
self._bucket = response.bucket;
600+
self.id = response.objectId;
601+
//Get the uptoken to upload files to qiniu.
602+
var uptoken = response.token;
603+
604+
var data = new FormData();
605+
data.append("file", self._blob, self._name);
606+
data.append("key", self._qiniu_key);
607+
data.append("token", uptoken);
608+
609+
var promise = new AV.Promise();
610+
var handled = false;
611+
612+
var xhr = new AV.XMLHttpRequest();
613+
614+
xhr.upload.addEventListener('progress', function(e) {
615+
if (e.lengthComputable) {
616+
saveOptions.onProgress && saveOptions.onProgress(e);
617+
}
618+
}, false);
619+
620+
xhr.onreadystatechange = function() {
621+
if (xhr.readyState === 4) {
622+
if (handled) {
623+
return;
624+
}
625+
handled = true;
626+
627+
delete self._qiniu_key;
628+
delete self._blob;
629+
if (xhr.status >= 200 && xhr.status < 300) {
630+
var response;
631+
try {
632+
response = JSON.parse(xhr.responseText);
633+
} catch (e) {
634+
promise.reject(e);
635+
self.destroy();
636+
}
637+
if (response) {
638+
promise.resolve(self);
639+
} else {
640+
promise.reject(response);
641+
}
642+
} else {
643+
promise.reject(xhr);
644+
self.destroy();
645+
}
646+
}
647+
};
648+
xhr.open('POST', 'http://upload.qiniu.com', true);
649+
xhr.send(data);
650+
556651
return promise;
557652
});
558653
} else {
@@ -563,7 +658,7 @@
563658
_ContentType: type,
564659
ACL: self._acl,
565660
mime_type: type,
566-
metaData: self._metaData,
661+
metaData: self._metaData
567662
};
568663
return AV._request("files", self._name, null, 'POST', data);
569664
}).then(function(response) {

test/file_blob.html

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<button onclick='upload ();'>upload</button>
2+
<progress id="progressBar" value="0"></progress>
3+
<html>
4+
<head>
5+
<title>Test</title>
6+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
8+
<link rel="stylesheet" href="../node_modules/mocha/mocha.css" />
9+
<script src="../node_modules/mocha/mocha.js"></script>
10+
<script src="../node_modules/expect.js/expect.js"></script>
11+
12+
<!--AV Test init-->
13+
14+
15+
<script type="text/javascript">var serverURL="http://stg.paas.com/";</script>
16+
<script src="../dist/av.js"></script>
17+
<script>
18+
var hasBlobConstructor = window.Blob && (function () {
19+
try {
20+
return Boolean(new Blob());
21+
} catch (e) {
22+
return false;
23+
}
24+
}()),
25+
hasArrayBufferViewSupport = hasBlobConstructor && window.Uint8Array &&
26+
(function () {
27+
try {
28+
return new Blob([new Uint8Array(100)]).size === 100;
29+
} catch (e) {
30+
return false;
31+
}
32+
}()),
33+
BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder ||
34+
window.MozBlobBuilder || window.MSBlobBuilder,
35+
dataURLtoBlob = (hasBlobConstructor || BlobBuilder) && window.atob &&
36+
window.ArrayBuffer && window.Uint8Array && function (dataURI) {
37+
var byteString,
38+
arrayBuffer,
39+
intArray,
40+
i,
41+
mimeString,
42+
bb;
43+
if (dataURI.split(',')[0].indexOf('base64') >= 0) {
44+
// Convert base64 to raw binary data held in a string:
45+
byteString = atob(dataURI.split(',')[1]);
46+
} else {
47+
// Convert base64/URLEncoded data component to raw binary data:
48+
byteString = decodeURIComponent(dataURI.split(',')[1]);
49+
}
50+
// Write the bytes of the string to an ArrayBuffer:
51+
arrayBuffer = new ArrayBuffer(byteString.length);
52+
intArray = new Uint8Array(arrayBuffer);
53+
for (i = 0; i < byteString.length; i += 1) {
54+
intArray[i] = byteString.charCodeAt(i);
55+
}
56+
// Separate out the mime component:
57+
mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
58+
// Write the ArrayBuffer (or ArrayBufferView) to a blob:
59+
if (hasBlobConstructor) {
60+
return new Blob(
61+
[hasArrayBufferViewSupport ? intArray : arrayBuffer],
62+
{type: mimeString}
63+
);
64+
}
65+
bb = new BlobBuilder();
66+
bb.append(arrayBuffer);
67+
return bb.getBlob(mimeString);
68+
};
69+
function upload (){
70+
AV.serverURL="https://cn-stg1.avoscloud.com";
71+
AV._initialize('mxrb5nn3qz7drek0etojy5lh4yrwjnk485lqajnsgjwfxrb5', 'd7sbus0d81mrum4tko4t8gl74b27vl0rh762ff7ngrb6ymmq', 'l0n9wu3kwnrtf2cg1b6w2l87nphzpypgff6240d0lxui2mm4');
72+
AV._useMasterKey = true;
73+
AV.setProduction(true);
74+
75+
var name = "photo.png";
76+
77+
var dataUrl = 'data:image/jpeg;base64,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';
78+
var blob = dataURLtoBlob(dataUrl);
79+
var avFile = new AV.File(name, {blob: blob});
80+
var progressBar = document.getElementById("progressBar");
81+
avFile.save({onProgress: function(e) {
82+
console.debug(e.loaded + '/' + e.total);
83+
progressBar.max = e.total;
84+
progressBar.value = e.loaded;
85+
}}, null).then(function() {
86+
console.info('File saved');
87+
console.dir(avFile);
88+
}, function(error) {
89+
console.error(error);
90+
});
91+
}
92+
</script>

0 commit comments

Comments
 (0)