Skip to content

Commit 465757f

Browse files
scottdallamurabryanmacfarlane
authored andcommitted
if the server gzips the file, unzip it (#102)
1 parent 0f0af8c commit 465757f

3 files changed

Lines changed: 38 additions & 30 deletions

File tree

api/FileContainerApi.ts

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
1212

1313
import stream = require("stream");
14+
import * as zlib from "zlib";
1415
import * as restm from 'typed-rest-client/RestClient';
1516
import * as httpm from 'typed-rest-client//HttpClient';
1617
import VsoBaseInterfaces = require('./interfaces/common/VsoBaseInterfaces');
@@ -55,15 +56,14 @@ export class FileContainerApi extends FileContainerApiBase.FileContainerApiBase
5556

5657
try {
5758
let verData: vsom.ClientVersioningData = await this.vsoClient.getVersioningData(
58-
"3.2-preview.4",
59+
"4.0-preview.4",
5960
"Container",
6061
"e4f5c81e-e250-447b-9fef-bd48471bea5e",
6162
routeValues,
6263
queryValues);
6364

6465
let url: string = verData.requestUrl;
65-
let options: restm.IRequestOptions = this.createRequestOptions('application/octet-stream',
66-
verData.apiVersion);
66+
let options: restm.IRequestOptions = this.createRequestOptions('application/octet-stream', verData.apiVersion);
6767

6868
let res = await this.http.get(url);
6969

@@ -99,7 +99,15 @@ export class FileContainerApi extends FileContainerApiBase.FileContainerApiBase
9999
reject(new Error(msg));
100100
}
101101
else {
102-
rres.result = res.message;
102+
// if the response is gzipped, unzip it
103+
if (res.message.headers["content-encoding"] === "gzip") {
104+
let unzipStream = zlib.createGunzip();
105+
res.message.pipe(unzipStream);
106+
rres.result = unzipStream;
107+
}
108+
else {
109+
rres.result = res.message;
110+
}
103111
resolve(rres);
104112
}
105113
}
@@ -112,15 +120,15 @@ export class FileContainerApi extends FileContainerApiBase.FileContainerApiBase
112120
public createItem(contentStream: NodeJS.ReadableStream, uncompressedLength: number, containerId: number, itemPath: string, scope: string, options: any): Promise<FileContainerInterfaces.FileContainerItem> {
113121
return new Promise<FileContainerInterfaces.FileContainerItem>((resolve, reject) => {
114122
let chunkStream = new ChunkStream(this, uncompressedLength, containerId, itemPath, scope, options);
115-
123+
116124
chunkStream.on('finish', () => {
117125
resolve(chunkStream.getItem());
118126
});
119-
127+
120128
contentStream.pipe(chunkStream);
121129
});
122130
}
123-
131+
124132
// used by ChunkStream
125133
public _createItem(
126134
customHeaders: VsoBaseInterfaces.IHeaders,
@@ -144,7 +152,7 @@ export class FileContainerApi extends FileContainerApiBase.FileContainerApiBase
144152
customHeaders["Content-Type"] = "";
145153

146154

147-
this.vsoClient.getVersioningData("3.2-preview.4", "Container", "e4f5c81e-e250-447b-9fef-bd48471bea5e", routeValues, queryValues)
155+
this.vsoClient.getVersioningData("4.0-preview.4", "Container", "e4f5c81e-e250-447b-9fef-bd48471bea5e", routeValues, queryValues)
148156
.then((versioningData: vsom.ClientVersioningData) => {
149157
var url: string = versioningData.requestUrl;
150158
var serializationData = { responseTypeMetadata: FileContainerInterfaces.TypeInfo.FileContainerItem, responseIsCollection: false };
@@ -170,24 +178,24 @@ export class FileContainerApi extends FileContainerApiBase.FileContainerApiBase
170178

171179
class ChunkStream extends stream.Writable {
172180
private static ChunkSize: number = (16 * 1024 * 1024);
173-
181+
174182
private _options: any;
175183
private _api: FileContainerApi;
176184
private _buffer: Buffer = new Buffer(ChunkStream.ChunkSize);
177-
185+
178186
private _length: number = 0;
179187
private _startRange: number = 0;
180188
private _bytesToSend: number = 0;
181189
private _totalReceived: number = 0;
182-
190+
183191
private _uncompressedLength: number;
184-
192+
185193
private _containerId: number;
186194
private _itemPath: string;
187195
private _scope: string;
188-
196+
189197
private _item: FileContainerInterfaces.FileContainerItem;
190-
198+
191199
constructor(api: FileContainerApi, uncompressedLength: number, containerId: number, itemPath: string, scope: string, options: any) {
192200
super();
193201
this._api = api;
@@ -196,10 +204,10 @@ class ChunkStream extends stream.Writable {
196204
this._containerId = containerId;
197205
this._itemPath = itemPath;
198206
this._scope = scope;
199-
207+
200208
this._bytesToSend = this._options.isGzipped ? this._options.compressedLength : uncompressedLength;
201209
}
202-
210+
203211
_write(data: Buffer | string, encoding: string, callback: Function): void {
204212
let chunk: Buffer = <Buffer>data;
205213
if (!chunk) {
@@ -212,7 +220,7 @@ class ChunkStream extends stream.Writable {
212220
}
213221
return;
214222
}
215-
223+
216224
let newBuffer: Buffer = null;
217225
if (this._length + chunk.length > ChunkStream.ChunkSize) {
218226
// overflow
@@ -234,22 +242,22 @@ class ChunkStream extends stream.Writable {
234242
callback();
235243
}
236244
}
237-
245+
238246
private _sendChunk(callback: Function, newBuffer?: Buffer) {
239247
let endRange = this._startRange + this._length;
240248
let headers = {
241249
"Content-Range": "bytes " + this._startRange + "-" + (endRange - 1) + "/" + this._bytesToSend,
242250
"Content-Length": this._length
243251
};
244-
252+
245253
if (this._options.isGzipped) {
246254
headers["Accept-Encoding"] = "gzip";
247255
headers["Content-Encoding"] = "gzip";
248256
headers["x-tfs-filelength"] = this._uncompressedLength;
249257
}
250-
258+
251259
this._startRange = endRange;
252-
260+
253261
this._api._createItem(headers, new BufferStream(this._buffer, this._length), this._containerId, this._itemPath, this._scope, (err: any, statusCode: number, item: FileContainerInterfaces.FileContainerItem) => {
254262
if (newBuffer) {
255263
this._length = newBuffer.length;
@@ -258,13 +266,13 @@ class ChunkStream extends stream.Writable {
258266
else {
259267
this._length = 0;
260268
}
261-
269+
262270
this._item = item;
263271

264272
callback(err);
265273
});
266274
}
267-
275+
268276
public getItem(): FileContainerInterfaces.FileContainerItem {
269277
return this._item;
270278
}
@@ -274,19 +282,19 @@ class BufferStream extends stream.Readable {
274282
private _buffer: Buffer;
275283
private _position: number = 0;
276284
private _length: number = 0;
277-
285+
278286
constructor(buffer: Buffer, length: number) {
279287
super();
280288
this._buffer = buffer;
281289
this._length = length;
282290
}
283-
291+
284292
_read(size: number): void {
285293
if (this._position >= this._length) {
286294
this.push(null);
287295
return;
288296
}
289-
297+
290298
let end: number = Math.min(this._position + size, this._length);
291299
this.push(this._buffer.slice(this._position, end));
292300
this._position = end;

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "vso-node-api",
33
"description": "Node client for Visual Studio Online/TFS REST APIs",
4-
"version": "6.2.3-preview",
4+
"version": "6.2.4-preview",
55
"main": "./WebApi.js",
66
"typings": "./WebApi.d.ts",
77
"scripts": {

samples/filecontainer.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ export async function run() {
1818
if (containers.length > 0) {
1919
let container = containers[0];
2020
console.log("found container " + container.name);
21+
let containerId = container.id;
2122

22-
let items = await fileContainerApi.getItems(container.id, null, null, null, null, null, null, false);
23+
let items = await fileContainerApi.getItems(containerId, null, null, null, null, null, null, false);
2324
console.log("found " + items.length + " items");
2425

2526
let item = items.filter((item) => {
@@ -28,7 +29,7 @@ export async function run() {
2829

2930
if (item) {
3031
console.log("downloading " + item.path);
31-
let restResponse = await fileContainerApi.getItem(container.id, null, item.path, item.path.substring(item.path.lastIndexOf('/') + 1));
32+
let restResponse = await fileContainerApi.getItem(containerId, null, item.path, item.path.substring(item.path.lastIndexOf('/') + 1));
3233

3334
let output = "";
3435
await new Promise((resolve, reject) => {
@@ -42,7 +43,6 @@ export async function run() {
4243
console.log("downloaded " + item.path);
4344
console.log(output);
4445
}
45-
4646
}
4747
}
4848
catch (err) {

0 commit comments

Comments
 (0)