Skip to content

Commit 2990242

Browse files
authored
Support byte range requests (#71)
* Support Byte Range Requests See parse-community/parse-server#6028 * use promises * Add Tests * rename getFileStream to handleFileStream
1 parent 90ba45c commit 2990242

File tree

2 files changed

+110
-0
lines changed

2 files changed

+110
-0
lines changed

index.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,5 +144,33 @@ S3Adapter.prototype.getFileLocation = function(config, filename) {
144144
return (config.mount + '/files/' + config.applicationId + '/' + filename);
145145
}
146146

147+
S3Adapter.prototype.handleFileStream = function (filename, req, res) {
148+
const params = {
149+
Key: this._bucketPrefix + filename,
150+
Range: req.get('Range'),
151+
};
152+
return this.createBucket().then(() => {
153+
return new Promise((resolve, reject) => {
154+
this._s3Client.getObject(params, (error, data) => {
155+
if (error !== null) {
156+
return reject(error);
157+
}
158+
if (data && !data.Body) {
159+
return reject(data);
160+
}
161+
res.writeHead(206, {
162+
'Accept-Ranges': data.AcceptRanges,
163+
'Content-Length': data.ContentLength,
164+
'Content-Range': data.ContentRange,
165+
'Content-Type': data.ContentType,
166+
});
167+
res.write(data.Body);
168+
res.end();
169+
resolve(data.Body);
170+
});
171+
});
172+
});
173+
}
174+
147175
module.exports = S3Adapter;
148176
module.exports.default = S3Adapter;

spec/test.spec.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,88 @@ describe('S3Adapter tests', () => {
153153
});
154154
});
155155

156+
describe('getFileStream', () => {
157+
it('should handle range bytes', () => {
158+
const s3 = new S3Adapter('accessKey', 'secretKey', 'myBucket');
159+
s3._s3Client = {
160+
createBucket: callback => callback(),
161+
getObject: (params, callback) => {
162+
const { Range } = params;
163+
164+
expect(Range).toBe('bytes=0-1');
165+
166+
const data = {
167+
Body: Buffer.from('hello world', 'utf8'),
168+
};
169+
callback(null, data);
170+
},
171+
};
172+
const req = {
173+
get: () => 'bytes=0-1',
174+
};
175+
const resp = {
176+
writeHead: jasmine.createSpy('writeHead'),
177+
write: jasmine.createSpy('write'),
178+
end: jasmine.createSpy('end'),
179+
};
180+
s3.handleFileStream('test.mov', req, resp).then((data) => {
181+
expect(data.toString('utf8')).toBe('hello world');
182+
expect(resp.writeHead).toHaveBeenCalled();
183+
expect(resp.write).toHaveBeenCalled();
184+
expect(resp.end).toHaveBeenCalled();
185+
});
186+
});
187+
188+
it('should handle range bytes error', () => {
189+
const s3 = new S3Adapter('accessKey', 'secretKey', 'myBucket');
190+
s3._s3Client = {
191+
createBucket: callback => callback(),
192+
getObject: (params, callback) => {
193+
callback('FileNotFound', null);
194+
},
195+
};
196+
const req = {
197+
get: () => 'bytes=0-1',
198+
};
199+
const resp = {
200+
writeHead: jasmine.createSpy('writeHead'),
201+
write: jasmine.createSpy('write'),
202+
end: jasmine.createSpy('end'),
203+
};
204+
s3.handleFileStream('test.mov', req, resp).catch((error) => {
205+
expect(error).toBe('FileNotFound');
206+
expect(resp.writeHead).not.toHaveBeenCalled();
207+
expect(resp.write).not.toHaveBeenCalled();
208+
expect(resp.end).not.toHaveBeenCalled();
209+
});
210+
});
211+
212+
it('should handle range bytes no data', () => {
213+
const s3 = new S3Adapter('accessKey', 'secretKey', 'myBucket');
214+
const data = { Error: 'NoBody' };
215+
s3._s3Client = {
216+
createBucket: callback => callback(),
217+
getObject: (params, callback) => {
218+
callback(null, data);
219+
},
220+
};
221+
const req = {
222+
get: () => 'bytes=0-1',
223+
};
224+
const resp = {
225+
writeHead: jasmine.createSpy('writeHead'),
226+
write: jasmine.createSpy('write'),
227+
end: jasmine.createSpy('end'),
228+
};
229+
s3.handleFileStream('test.mov', req, resp).catch((error) => {
230+
expect(error).toBe(data);
231+
expect(resp.writeHead).not.toHaveBeenCalled();
232+
expect(resp.write).not.toHaveBeenCalled();
233+
expect(resp.end).not.toHaveBeenCalled();
234+
});
235+
});
236+
});
237+
156238
describe('getFileLocation', () => {
157239
var config = {
158240
mount: 'http://my.server.com/parse',

0 commit comments

Comments
 (0)