file_stream.js 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. 'use strict';
  2. const fs = require('fs');
  3. const path = require('path');
  4. const stream = require('stream');
  5. const tar = require('tar-stream');
  6. const utils = require('../utils');
  7. const ready = require('get-ready');
  8. class TarFileStream extends stream.Transform {
  9. constructor(opts) {
  10. super(opts);
  11. const pack = tar.pack();
  12. pack.on('data', chunk => this.push(chunk));
  13. pack.on('end', () => this.ready(true));
  14. const sourceType = utils.sourceType(opts.source);
  15. if (sourceType === 'file') {
  16. // stat file to get file size
  17. fs.stat(opts.source, (err, stat) => {
  18. if (err) return this.emit('error', err);
  19. this.entry = pack.entry({ name: opts.relativePath || path.basename(opts.source), size: stat.size, mode: stat.mode & 0o777 }, err => {
  20. if (err) return this.emit('error', err);
  21. pack.finalize();
  22. });
  23. const stream = fs.createReadStream(opts.source, opts.fs);
  24. stream.on('error', err => this.emit('error', err));
  25. stream.pipe(this);
  26. });
  27. } else if (sourceType === 'buffer') {
  28. if (!opts.relativePath) return this.emit('error', 'opts.relativePath is required if opts.source is a buffer');
  29. pack.entry({ name: opts.relativePath }, opts.source);
  30. pack.finalize();
  31. this.end();
  32. } else { // stream or undefined
  33. if (!opts.relativePath) return process.nextTick(() => this.emit('error', 'opts.relativePath is required'));
  34. if (opts.size) {
  35. this.entry = pack.entry({ name: opts.relativePath, size: opts.size }, err => {
  36. if (err) return this.emit('error', err);
  37. pack.finalize();
  38. });
  39. } else {
  40. if (!opts.suppressSizeWarning) {
  41. console.warn('You should specify the size of streamming data by opts.size to prevent all streaming data from loading into memory. If you are sure about memory cost, pass opts.suppressSizeWarning: true to suppress this warning');
  42. }
  43. const buf = [];
  44. this.entry = new stream.Writable({
  45. write(chunk, _, callback) {
  46. buf.push(chunk);
  47. callback();
  48. },
  49. });
  50. this.entry.on('finish', () => {
  51. pack.entry({ name: opts.relativePath }, Buffer.concat(buf));
  52. pack.finalize();
  53. });
  54. }
  55. if (sourceType === 'stream') {
  56. opts.source.on('error', err => this.emit('error', err));
  57. opts.source.pipe(this);
  58. }
  59. }
  60. }
  61. _transform(chunk, encoding, callback) {
  62. if (this.entry) {
  63. this.entry.write(chunk, encoding, callback);
  64. }
  65. }
  66. _flush(callback) {
  67. if (this.entry) {
  68. this.entry.end();
  69. }
  70. this.ready(callback);
  71. }
  72. }
  73. ready.mixin(TarFileStream.prototype);
  74. module.exports = TarFileStream;