You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.js 2.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. 'use strict';
  2. const fs = require('fs');
  3. const path = require('path');
  4. const url = require('url');
  5. const caw = require('caw');
  6. const contentDisposition = require('content-disposition');
  7. const decompress = require('decompress');
  8. const filenamify = require('filenamify');
  9. const getStream = require('get-stream');
  10. const got = require('got');
  11. const makeDir = require('make-dir');
  12. const pify = require('pify');
  13. const pEvent = require('p-event');
  14. const fileType = require('file-type');
  15. const extName = require('ext-name');
  16. const fsP = pify(fs);
  17. const filenameFromPath = res => path.basename(url.parse(res.requestUrl).pathname);
  18. const getExtFromMime = res => {
  19. const header = res.headers['content-type'];
  20. if (!header) {
  21. return null;
  22. }
  23. const exts = extName.mime(header);
  24. if (exts.length !== 1) {
  25. return null;
  26. }
  27. return exts[0].ext;
  28. };
  29. const getFilename = (res, data) => {
  30. const header = res.headers['content-disposition'];
  31. if (header) {
  32. const parsed = contentDisposition.parse(header);
  33. if (parsed.parameters && parsed.parameters.filename) {
  34. return parsed.parameters.filename;
  35. }
  36. }
  37. let filename = filenameFromPath(res);
  38. if (!path.extname(filename)) {
  39. const ext = (fileType(data) || {}).ext || getExtFromMime(res);
  40. if (ext) {
  41. filename = `${filename}.${ext}`;
  42. }
  43. }
  44. return filename;
  45. };
  46. module.exports = (uri, output, opts) => {
  47. if (typeof output === 'object') {
  48. opts = output;
  49. output = null;
  50. }
  51. let protocol = url.parse(uri).protocol;
  52. if (protocol) {
  53. protocol = protocol.slice(0, -1);
  54. }
  55. opts = Object.assign({
  56. encoding: null,
  57. rejectUnauthorized: process.env.npm_config_strict_ssl !== 'false'
  58. }, opts);
  59. const agent = caw(opts.proxy, {protocol});
  60. const stream = got.stream(uri, Object.assign({agent}, opts));
  61. const promise = pEvent(stream, 'response').then(res => {
  62. const encoding = opts.encoding === null ? 'buffer' : opts.encoding;
  63. return Promise.all([getStream(stream, {encoding}), res]);
  64. }).then(result => {
  65. // TODO: Use destructuring when targeting Node.js 6
  66. const data = result[0];
  67. const res = result[1];
  68. if (!output) {
  69. return opts.extract ? decompress(data, opts) : data;
  70. }
  71. const filename = opts.filename || filenamify(getFilename(res, data));
  72. const outputFilepath = path.join(output, filename);
  73. if (opts.extract) {
  74. return decompress(data, path.dirname(outputFilepath), opts);
  75. }
  76. return makeDir(path.dirname(outputFilepath))
  77. .then(() => fsP.writeFile(outputFilepath, data))
  78. .then(() => data);
  79. });
  80. stream.then = promise.then.bind(promise);
  81. stream.catch = promise.catch.bind(promise);
  82. return stream;
  83. };