Skip to content

Commit fe05d70

Browse files
authored
(Browser) Support for File Uri Upload (#765)
* (Browser) Support for File Uri Upload * add proper response * improve browser support * xhr not exist in browser * clean up * revert for react-native
1 parent 45658b9 commit fe05d70

File tree

2 files changed

+96
-7
lines changed

2 files changed

+96
-7
lines changed

src/ParseFile.js

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@
88
*
99
* @flow
1010
*/
11-
/* global File */
11+
/* global XMLHttpRequest, File */
1212
import CoreManager from './CoreManager';
1313
import type { FullOptions } from './RESTController';
14-
const http = require('http');
15-
const https = require('https');
14+
15+
let XHR = null;
16+
if (typeof XMLHttpRequest !== 'undefined') {
17+
XHR = XMLHttpRequest;
18+
}
1619

1720
type Base64 = { base64: string };
1821
type FileData = Array<number> | Base64 | File;
@@ -311,11 +314,16 @@ const DefaultController = {
311314
},
312315

313316
download: function(uri) {
317+
if (XHR) {
318+
return this.downloadAjax(uri);
319+
}
320+
if (process.env.PARSE_BUILD === 'browser') {
321+
return Promise.reject('Cannot make a request: No definition of XMLHttpRequest was found.');
322+
}
314323
return new Promise((resolve, reject) => {
315-
let client = http;
316-
if (uri.indexOf('https') === 0) {
317-
client = https;
318-
}
324+
const client = uri.indexOf('https') === 0
325+
? require('https')
326+
: require('http');
319327
client.get(uri, (resp) => {
320328
resp.setEncoding('base64');
321329
let base64 = '';
@@ -328,6 +336,30 @@ const DefaultController = {
328336
});
329337
}).on('error', reject);
330338
});
339+
},
340+
341+
downloadAjax: function(uri) {
342+
return new Promise((resolve, reject) => {
343+
const xhr = new XHR();
344+
xhr.open('GET', uri, true);
345+
xhr.responseType = 'arraybuffer';
346+
xhr.onerror = function(e) { reject(e); };
347+
xhr.onreadystatechange = function() {
348+
if (xhr.readyState !== 4) {
349+
return;
350+
}
351+
const bytes = new Uint8Array(this.response);
352+
resolve({
353+
base64: ParseFile.encodeBase64(bytes),
354+
contentType: xhr.getResponseHeader('content-type'),
355+
});
356+
};
357+
xhr.send();
358+
});
359+
},
360+
361+
_setXHR(xhr: any) {
362+
XHR = xhr;
331363
}
332364
};
333365

src/__tests__/ParseFile-test.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ describe('ParseFile', () => {
4141
});
4242
});
4343

44+
afterEach(() => {
45+
process.env.PARSE_BUILD = 'node';
46+
});
47+
4448
it('can create files with base64 encoding', () => {
4549
const file = new ParseFile('parse.txt', { base64: 'ParseA==' });
4650
expect(file._source.base64).toBe('ParseA==');
@@ -303,6 +307,7 @@ describe('FileController', () => {
303307
});
304308

305309
it('download with base64 http', async () => {
310+
defaultController._setXHR(null);
306311
const mockResponse = Object.create(EventEmitter.prototype);
307312
EventEmitter.call(mockResponse);
308313
mockResponse.setEncoding = function() {}
@@ -328,6 +333,7 @@ describe('FileController', () => {
328333
});
329334

330335
it('download with base64 https', async () => {
336+
defaultController._setXHR(null);
331337
const mockResponse = Object.create(EventEmitter.prototype);
332338
EventEmitter.call(mockResponse);
333339
mockResponse.setEncoding = function() {}
@@ -351,4 +357,55 @@ describe('FileController', () => {
351357
expect(mockHttps.get).toHaveBeenCalledTimes(1);
352358
spy.mockRestore();
353359
});
360+
361+
it('download with ajax', async () => {
362+
const mockXHR = function () {
363+
return {
364+
open: jest.fn(),
365+
send: jest.fn().mockImplementation(function() {
366+
this.response = [61, 170, 236, 120];
367+
this.readyState = 2;
368+
this.onreadystatechange();
369+
this.readyState = 4;
370+
this.onreadystatechange();
371+
}),
372+
getResponseHeader: function() {
373+
return 'image/png';
374+
}
375+
};
376+
};
377+
defaultController._setXHR(mockXHR);
378+
379+
const data = await defaultController.download('https://example.com/image.png');
380+
expect(data.base64).toBe('ParseA==');
381+
expect(data.contentType).toBe('image/png');
382+
});
383+
384+
it('download with ajax error', async () => {
385+
const mockXHR = function () {
386+
return {
387+
open: jest.fn(),
388+
send: jest.fn().mockImplementation(function() {
389+
this.onerror('error thrown');
390+
})
391+
};
392+
};
393+
defaultController._setXHR(mockXHR);
394+
395+
try {
396+
await defaultController.download('https://example.com/image.png');
397+
} catch (e) {
398+
expect(e).toBe('error thrown');
399+
}
400+
});
401+
402+
it('download with xmlhttprequest unsupported', async () => {
403+
defaultController._setXHR(null);
404+
process.env.PARSE_BUILD = 'browser';
405+
try {
406+
await defaultController.download('https://example.com/image.png');
407+
} catch (e) {
408+
expect(e).toBe('Cannot make a request: No definition of XMLHttpRequest was found.');
409+
}
410+
});
354411
});

0 commit comments

Comments
 (0)