1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
| const fs = require('fs'); const path = require('path'); const https = require('https'); const http = require('http'); const crypto = require('crypto');
const IMG_REGEX = /!\[.*?\]\((http.*?)\)/g; const mimeExtensions = { 'image/jpeg': '.jpg', 'image/png': '.png', 'image/gif': '.gif', 'image/webp': '.webp', 'image/svg+xml': '.svg', 'image/bmp': '.bmp', 'image/tiff': '.tiff' };
function getExtensionFromMime(mime) { return mimeExtensions[mime] || '.jpg'; }
function downloadImage(url, savePath) { return new Promise((resolve, reject) => { const mod = url.startsWith('https') ? https : http; mod.get(url, res => { if (res.statusCode !== 200) return reject(`状态码 ${res.statusCode}`); const ext = getExtensionFromMime(res.headers['content-type'] || ''); const filePath = savePath + ext; const fileStream = fs.createWriteStream(filePath); res.pipe(fileStream); fileStream.on('finish', () => { fileStream.close(() => resolve(filePath)); }); }).on('error', reject); }); }
function collectImageURLs(mdFiles) { const urlCountMap = new Map(); const fileMap = new Map();
for (const file of mdFiles) { const content = fs.readFileSync(file, 'utf8'); const urls = [...content.matchAll(IMG_REGEX)].map(m => m[1]); fileMap.set(file, urls); urls.forEach(url => { if (!url.startsWith('http')) return; urlCountMap.set(url, (urlCountMap.get(url) || 0) + 1); }); } return { urlCountMap, fileMap }; }
async function processFiles(mdFiles, urlCountMap, fileMap) { const sharedDir = './shared'; if (!fs.existsSync(sharedDir)) fs.mkdirSync(sharedDir);
const sharedMap = new Map(); let sharedIndex = 1;
for (const file of mdFiles) { const content = fs.readFileSync(file, 'utf8'); const baseName = path.basename(file, '.md'); const folder = `./${baseName}`; if (!fs.existsSync(folder)) fs.mkdirSync(folder);
let newContent = content; let uniqueIndex = 1; const urls = fileMap.get(file);
for (const url of urls) { if (!url.startsWith('http')) continue;
let newPath = '';
if (urlCountMap.get(url) > 1) { if (!sharedMap.has(url)) { const fileName = `img${sharedIndex++}`; try { const savedPath = await downloadImage(url, path.join(sharedDir, fileName)); const relPath = `shared/${path.basename(savedPath)}`; sharedMap.set(url, relPath); console.log(`✅ [共享] 下载 ${url} -> ${relPath}`); } catch (err) { console.log(`❌ 下载失败(共享)${url}: ${err}`); continue; } } newPath = sharedMap.get(url); } else { const fileName = `img${uniqueIndex++}`; try { const savedPath = await downloadImage(url, path.join(folder, fileName)); newPath = `${baseName}/${path.basename(savedPath)}`; console.log(`✅ [独占] 下载 ${url} -> ${newPath}`); } catch (err) { console.log(`❌ 下载失败(独占)${url}: ${err}`); continue; } }
newContent = newContent.replaceAll(url, newPath); }
fs.writeFileSync(file, newContent, 'utf8'); console.log(`📄 完成处理:${file}`); } }
(async () => { const mdFiles = fs.readdirSync('.').filter(f => f.endsWith('.md')); const { urlCountMap, fileMap } = collectImageURLs(mdFiles); await processFiles(mdFiles, urlCountMap, fileMap); console.log(`✅ 全部完成!`); })();
|