upload.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. import axios, {AxiosRequestConfig} from "axios";
  2. import chalk from "chalk"
  3. import fs from "fs";
  4. import ipfsHash from "ipfs-only-hash";
  5. import { ContentId, DataObject } from '@joystream/types/media';
  6. import BN from "bn.js";
  7. import { Option } from '@polkadot/types/codec';
  8. import Debug from "debug";
  9. const debug = Debug('joystream:storage-cli:upload');
  10. // Defines maximum content length for the assets (files). Limits the upload.
  11. const MAX_CONTENT_LENGTH = 500 * 1024 * 1024; // 500Mb
  12. function fail(message: string) {
  13. console.log(chalk.red(message));
  14. process.exit(1);
  15. }
  16. async function computeIpfsHash(filePath: string): Promise<string> {
  17. const file = fs.createReadStream(filePath)
  18. .on('error', (err) => {
  19. fail(`File read failed: ${err}`);
  20. });
  21. return await ipfsHash.of(file);
  22. }
  23. function getFileSize(filePath: string): number {
  24. const stats = fs.statSync(filePath);
  25. return stats.size;
  26. }
  27. //
  28. interface AddContentParams {
  29. accountId: string,
  30. ipfsCid: string,
  31. contentId: string,
  32. fileSize: BN,
  33. dataObjectTypeId: number,
  34. memberId: number
  35. }
  36. // Composes an asset URL and logs it to console.
  37. function createAndLogAssetUrl(url: string, contentId: string) : string {
  38. const assetUrl = `${url}/asset/v0/${contentId}`;
  39. console.log(chalk.yellow('Generated asset URL:', assetUrl));
  40. return assetUrl;
  41. }
  42. function getAccountId(api: any) : string {
  43. const ALICE_URI = '//Alice'
  44. return api.identities.keyring.addFromUri(ALICE_URI, null, 'sr25519').address
  45. }
  46. // Creates parameters for the AddContent runtime tx.
  47. async function getAddContentParams(api: any, filePath: string): Promise<AddContentParams> {
  48. const dataObjectTypeId = 1;
  49. const memberId = 0; // alice
  50. const accountId = getAccountId(api)
  51. return {
  52. accountId,
  53. ipfsCid: await computeIpfsHash(filePath),
  54. contentId: ContentId.generate().encode(),
  55. fileSize: new BN(getFileSize(filePath)),
  56. dataObjectTypeId,
  57. memberId
  58. };
  59. }
  60. // Creates the DataObject in the runtime.
  61. async function createContent(api: any, p: AddContentParams) : Promise<DataObject> {
  62. try {
  63. const dataObject: Option<DataObject> = await api.assets.createDataObject(
  64. p.accountId,
  65. p.memberId,
  66. p.contentId,
  67. p.dataObjectTypeId,
  68. p.fileSize,
  69. p.ipfsCid
  70. );
  71. if (dataObject.isNone) {
  72. fail("Cannot create data object: got None object");
  73. }
  74. return dataObject.unwrap();
  75. } catch (err) {
  76. fail(`Cannot create data object: ${err}`);
  77. }
  78. }
  79. // Command executor.
  80. export async function run(api: any, filePath: string){
  81. let addContentParams = await getAddContentParams(api, filePath);
  82. debug(`AddContent Tx params: ${addContentParams}`); // debug
  83. debug(`Decoded CID: ${ContentId.decode(addContentParams.contentId).toString()}`);
  84. let dataObject = await createContent(api, addContentParams);
  85. debug(`Received data object: ${dataObject.toString()}`); // debug
  86. let assetUrl = createAndLogAssetUrl("http://localhost:3001", addContentParams.contentId);
  87. await uploadFile(assetUrl, filePath);
  88. }
  89. // Uploads file to given asset URL.
  90. async function uploadFile(assetUrl: string, filePath: string) {
  91. // Create file read stream and set error handler.
  92. const file = fs.createReadStream(filePath)
  93. .on('error', (err) => {
  94. fail(`File read failed: ${err}`);
  95. });
  96. // Upload file from the stream.
  97. try {
  98. const fileSize = getFileSize(filePath);
  99. const config: AxiosRequestConfig = {
  100. headers: {
  101. 'Content-Type': '', // https://github.com/Joystream/storage-node-joystream/issues/16
  102. 'Content-Length': fileSize.toString()
  103. },
  104. 'maxContentLength': MAX_CONTENT_LENGTH,
  105. };
  106. await axios.put(assetUrl, file, config);
  107. console.log("File uploaded.");
  108. } catch (err) {
  109. fail(err.toString());
  110. }
  111. }