analyze-oss-aug.cjs 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. // 列 OSS audio/ 下,按月分桶,看每个月的 key 模式分布
  2. const fs = require('fs');
  3. const path = require('path');
  4. const envTxt = fs.readFileSync(path.join(__dirname, '..', '..', 'server', '.env.production'), 'utf8');
  5. const env = {};
  6. for (const line of envTxt.split(/\r?\n/)) {
  7. const m = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)\s*$/);
  8. if (m) env[m[1]] = m[2].replace(/^["']|["']$/g, '');
  9. }
  10. const OSS = require(path.join(__dirname, '..', '..', 'server', 'node_modules', 'ali-oss'));
  11. const client = new OSS({ accessKeyId: env.OSS_ACCESS_KEY_ID, accessKeySecret: env.OSS_ACCESS_KEY_SECRET, bucket: 'rrbrr-book-audio', endpoint: 'oss-cn-shanghai.aliyuncs.com', secure: true });
  12. async function listAll(prefix) {
  13. const all = [];
  14. let marker = null;
  15. do {
  16. const r = await client.list({ prefix, 'max-keys': 1000, marker });
  17. all.push(...(r.objects || []));
  18. marker = r.nextMarker || null;
  19. } while (marker);
  20. return all;
  21. }
  22. function fmt(b) { return (b/1024/1024).toFixed(1) + ' MB'; }
  23. (async () => {
  24. const all = await listAll('audio/');
  25. // 按月 + key pattern 分类
  26. const byMonthPattern = new Map();
  27. for (const o of all) {
  28. const m = (o.lastModified || '').slice(0, 7); // YYYY-MM
  29. const key = o.name;
  30. // 提取关键模式
  31. let pattern;
  32. if (key.startsWith('audio/merged/')) pattern = 'audio/merged/...';
  33. else {
  34. const parts = key.split('/');
  35. const second = parts[1] || '';
  36. if (second.startsWith('preview-')) pattern = 'audio/preview-uuid/...';
  37. else if (second.startsWith('sync-')) pattern = 'audio/sync-uuid/...';
  38. else if (/^[0-9a-f-]{36}$/.test(second)) {
  39. const fname = parts[2] || '';
  40. if (fname.includes('_merged')) pattern = 'audio/uuid/{uuid}_merged.{ext}';
  41. else if (fname.includes('output')) pattern = 'audio/uuid/output.{ext}';
  42. else if (fname.includes('cloud_segment')) pattern = 'audio/uuid/cloud_segment_*.{ext}';
  43. else pattern = `audio/uuid/${fname.split('.')[0]}.{ext}`;
  44. } else if (/^\d+$/.test(second)) pattern = 'audio/{numeric}/...'; // 比如 chapterId?
  45. else pattern = `audio/${second.substring(0,20)}/...`;
  46. }
  47. const k = `${m}|${pattern}`;
  48. const cur = byMonthPattern.get(k) || { count: 0, bytes: 0 };
  49. cur.count++; cur.bytes += o.size || 0;
  50. byMonthPattern.set(k, cur);
  51. }
  52. // 按月输出
  53. const months = [...new Set([...byMonthPattern.keys()].map(k => k.split('|')[0]))].sort();
  54. for (const mo of months) {
  55. console.log(`\n=== ${mo} ===`);
  56. const arr = [...byMonthPattern.entries()].filter(([k]) => k.startsWith(mo)).sort((a, b) => b[1].bytes - a[1].bytes);
  57. for (const [k, v] of arr) {
  58. const pat = k.split('|')[1];
  59. console.log(` ${String(v.count).padStart(7)} ${fmt(v.bytes).padStart(10)} ${pat}`);
  60. }
  61. }
  62. })();