Skip to content

Commit 0244f4e

Browse files
committed
fix(update):
修复腾讯云图床/Github图床上传问题 图片上传失败时,取消停止进程 新增图片上传时的并发数concurrency,使用Github图床时,并发问题严重,建议设置为1
1 parent c0ebf1e commit 0244f4e

File tree

10 files changed

+95
-58
lines changed

10 files changed

+95
-58
lines changed

README.md

+7
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ A downloader for articles from yuque(语雀知识库同步工具)
8080
"lastGeneratePath": "lastGeneratePath.log",
8181
"imgCdn": {
8282
"enabled": false,
83+
"concurrency": 0,
8384
"imageBed": "qiniu",
8485
"host": "",
8586
"bucket": "",
@@ -113,6 +114,7 @@ imgCdn 语雀图片转图床配置说明
113114
| 参数名 | 含义 | 默认值 |
114115
|-----------|-----------------------------------------------------------------------------------------|---------|
115116
| enabled | 是否开启 | false |
117+
| concurrency | 上传图片并发数, 0代表无限制,使用github图床时,并发问题严重,建议设置为1 | 0 |
116118
| imageBed | 选择将图片上传的图床<br/>目前支持腾讯云(cos)、阿里云(oss)和七牛云(qiniu),又拍云(upyun),Github图床(github)<br/>默认使用七牛云 | 'qiniu' |
117119
| host | 使用七牛云/又拍云图床时,需要指定CDN域名前缀 | |
118120
| bucket | 图床的bucket名称 | - |
@@ -233,6 +235,11 @@ DEBUG=yuque-hexo.* yuque-hexo sync
233235

234236
# Changelog
235237

238+
### v1.9.5
239+
- 修复腾讯云图床/Github图床上传问题
240+
- 图片上传失败时,取消停止进程
241+
- 新增图片上传时的并发数concurrency,使用Github图床时,并发问题严重,建议设置为1
242+
236243
### v1.9.4
237244
- 🔥 新增GitHub图床和又拍云图床
238245

config.js

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const defaultConfig = {
2020
onlyPublished: false,
2121
onlyPublic: false,
2222
imgCdn: {
23+
concurrency: 0,
2324
enabled: false,
2425
imageBed: 'qiniu',
2526
host: '',

lib/Downloader.js

+14-3
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,21 @@ class Downloader {
210210
const { _cachedArticles, postBasicPath } = this;
211211
mkdirp.sync(postBasicPath);
212212
out.info(`create posts directory (if it not exists): ${postBasicPath}`);
213-
const promiseList = _cachedArticles.map(async item => {
214-
return await this.generatePost(item);
213+
const promiseList = _cachedArticles.map(item => {
214+
return async () => {
215+
return await this.generatePost(item);
216+
};
217+
});
218+
// 并发数
219+
const concurrency = this.config.imgCdn.concurrency || promiseList.length;
220+
const queue = new Queue({ concurrency });
221+
queue.push(...promiseList);
222+
await new Promise((resolve, reject) => {
223+
queue.start(function(err) {
224+
if (err) return reject(err);
225+
resolve();
226+
});
215227
});
216-
await Promise.all(promiseList);
217228
}
218229

219230
// 文章下载 => 增量更新文章到缓存 json 文件 => 全量生成 markdown 文章

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "yuque-hexo",
3-
"version": "1.9.4",
3+
"version": "1.9.5",
44
"description": "A downloader for articles from yuque",
55
"main": "index.js",
66
"scripts": {

util/imageBeds/cos.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// 腾讯云图床
44
const COS = require('cos-nodejs-sdk-v5');
55
const out = require('../../lib/out');
6-
const { transformRes } = require('../index');
76

87
const secretId = process.env.SECRET_ID;
98
const secretKey = process.env.SECRET_KEY;
@@ -40,7 +39,6 @@ class CosClient {
4039
});
4140
return `https://${this.config.bucket}.cos.${this.config.region}.myqcloud.com/${this.config.prefixKey}/${fileName}`;
4241
} catch (e) {
43-
out.warn(`检查图片信息时出错: ${transformRes(e)}`);
4442
return '';
4543
}
4644
}
@@ -63,8 +61,8 @@ class CosClient {
6361
});
6462
return `https://${res.Location}`;
6563
} catch (e) {
66-
out.error(`上传图片失败,请检查: ${transformRes(e)}`);
67-
process.exit(-1);
64+
out.warn(`上传图片失败,请检查: ${e}`);
65+
return '';
6866
}
6967
}
7068
}

util/imageBeds/github.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ class GithubClient {
5151
Authorization: `token ${secretKey}`,
5252
},
5353
});
54+
if (result.status === 409) {
55+
out.warn('由于github并发问题,图片上传失败');
56+
return '';
57+
}
5458
if (result.status === 200 || result.status === 201) {
5559
if (this.config.host) {
5660
return `${this.config.host}/gh/${secretId}/${this.config.bucket}/${this.config.prefixKey}/${fileName}`;
@@ -96,11 +100,8 @@ class GithubClient {
96100
const base64File = imgBuffer.toString('base64');
97101
const imgUrl = await this._fetch('PUT', fileName, base64File);
98102
if (imgUrl) return imgUrl;
99-
out.error('上传图片失败,请检查');
100-
process.exit(-1);
101103
} catch (e) {
102-
out.error(`上传图片失败,请检查: ${transformRes(e)}`);
103-
process.exit(-1);
104+
out.warn(`上传图片失败,请检查: ${e}`);
104105
}
105106
}
106107
}

util/imageBeds/oss.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ class OssClient {
5757
const res = await this.imageBedInstance.put(`${this.config.prefixKey}/${fileName}`, imgBuffer);
5858
return res.url;
5959
} catch (e) {
60-
out.error(`上传图片失败,请检查: ${transformRes(e)}`);
61-
process.exit(-1);
60+
out.warn(`上传图片失败,请检查: ${e}`);
61+
return '';
6262
}
6363
}
6464
}

util/imageBeds/qiniu.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,14 @@ class QiniuClient {
7272
this.formUploader.put(this.uploadToken, `${this.config.prefixKey}/${fileName}`, imgBuffer, this.putExtra, (respErr,
7373
respBody, respInfo) => {
7474
if (respErr) {
75-
out.error(`上传图片失败,请检查: ${transformRes(respErr)}`);
76-
process.exit(-1);
75+
out.warn(`上传图片失败,请检查: ${transformRes(respErr)}`);
76+
resolve('');
7777
}
7878
if (respInfo.statusCode === 200) {
7979
resolve(`${this.config.host}/${this.config.prefixKey}/${fileName}`);
8080
} else {
81-
out.error(`上传图片失败,请检查: ${transformRes(respInfo)}`);
82-
process.exit(-1);
81+
out.warn(`上传图片失败,请检查: ${transformRes(respInfo)}`);
82+
resolve('');
8383
}
8484
});
8585
});

util/imageBeds/upyun.js

+5-6
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// 又拍云图床
44
const upyun = require('upyun');
55
const out = require('../../lib/out');
6-
const { transformRes } = require('../index');
76

87
const secretId = process.env.SECRET_ID;
98
const secretKey = process.env.SECRET_KEY;
@@ -47,7 +46,7 @@ class UPClient {
4746
}
4847
return '';
4948
} catch (e) {
50-
out.error(`上传图片失败,请检查: ${transformRes(e)}`);
49+
out.warn(`上传图片失败,请检查: ${e}`);
5150
return '';
5251
}
5352
}
@@ -65,11 +64,11 @@ class UPClient {
6564
if (res) {
6665
return `${this.config.host}/${this.config.prefixKey}/${fileName}`;
6766
}
68-
out.error('上传图片失败,请检查又拍云配置');
69-
process.exit(-1);
67+
out.warn('上传图片失败,请检查又拍云配置');
68+
return '';
7069
} catch (e) {
71-
out.error(`上传图片失败,请检查: ${transformRes(e)}`);
72-
process.exit(-1);
70+
out.warn(`上传图片失败,请检查: ${e}`);
71+
return '';
7372
}
7473
}
7574
}

util/img2cdn.js

+54-34
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ const getEtag = require('../lib/qetag');
55
const config = require('../config');
66
const out = require('../lib/out');
77
const ImageBed = require('./imageBeds');
8+
const Queue = require('queue');
9+
const lodash = require('lodash');
810

911
const imageBed = config.imgCdn.enabled ? ImageBed.getInstance(config.imgCdn) : null;
1012

@@ -83,49 +85,67 @@ async function img2Cdn(article) {
8385
// 1。从文章中获取语雀的图片URL列表
8486
const matchYuqueImgUrlList = article.body.match(imageUrlRegExp);
8587
if (!matchYuqueImgUrlList) return article;
86-
const promiseList = matchYuqueImgUrlList.map(async matchYuqueImgUrl => {
87-
// 获取真正的图片url
88-
const yuqueImgUrl = getImgUrl(matchYuqueImgUrl);
89-
// 2。将图片转成buffer
90-
const imgBuffer = await img2Buffer(yuqueImgUrl);
91-
if (!imgBuffer) {
92-
return {
93-
originalUrl: matchYuqueImgUrl,
94-
yuqueRealImgUrl: yuqueImgUrl,
95-
url: yuqueImgUrl,
96-
};
97-
}
98-
// 3。根据buffer文件生成唯一的hash文件名
99-
const fileName = await getFileName(imgBuffer, yuqueImgUrl);
100-
try {
101-
// 4。检查图床是否存在该文件
102-
let url = await imageBed.hasImage(fileName);
103-
let exists = true;
104-
// 5。如果图床已经存在,直接替换;如果图床不存在,则先上传到图床,再将原本的语雀url进行替换
105-
if (!url) {
106-
url = await imageBed.uploadImg(imgBuffer, fileName);
107-
exists = false;
88+
const promiseList = matchYuqueImgUrlList.map(matchYuqueImgUrl => {
89+
return async () => {
90+
// 获取真正的图片url
91+
const yuqueImgUrl = getImgUrl(matchYuqueImgUrl);
92+
// 2。将图片转成buffer
93+
const imgBuffer = await img2Buffer(yuqueImgUrl);
94+
if (!imgBuffer) {
95+
return {
96+
originalUrl: matchYuqueImgUrl,
97+
yuqueRealImgUrl: yuqueImgUrl,
98+
url: yuqueImgUrl,
99+
};
108100
}
109-
return {
110-
originalUrl: matchYuqueImgUrl,
111-
yuqueRealImgUrl: yuqueImgUrl,
112-
url,
113-
exists,
114-
};
115-
} catch (e) {
116-
out.error(`访问图床出错,请检查配置: ${e}`);
117-
process.exit(-1);
118-
}
101+
// 3。根据buffer文件生成唯一的hash文件名
102+
const fileName = await getFileName(imgBuffer, yuqueImgUrl);
103+
try {
104+
// 4。检查图床是否存在该文件
105+
let url = await imageBed.hasImage(fileName);
106+
let exists = true;
107+
// 5。如果图床已经存在,直接替换;如果图床不存在,则先上传到图床,再将原本的语雀url进行替换
108+
if (!url) {
109+
url = await imageBed.uploadImg(imgBuffer, fileName);
110+
exists = false;
111+
}
112+
return {
113+
originalUrl: matchYuqueImgUrl,
114+
yuqueRealImgUrl: yuqueImgUrl,
115+
url,
116+
exists,
117+
};
118+
} catch (e) {
119+
out.error(`访问图床出错,请检查配置: ${e}`);
120+
return {
121+
yuqueRealImgUrl: yuqueImgUrl,
122+
url: '',
123+
};
124+
}
125+
};
126+
});
127+
// 并发数
128+
const concurrency = config.imgCdn.concurrency || promiseList.length;
129+
const queue = new Queue({ concurrency, results: [] });
130+
queue.push(...promiseList);
131+
await new Promise(resolve => {
132+
queue.start(() => {
133+
resolve();
134+
});
119135
});
120-
const urlList = await Promise.all(promiseList);
136+
const _urlList = queue.results;
137+
const urlList = lodash.flatten(_urlList);
138+
121139
urlList.forEach(function(url) {
122-
if (url) {
140+
if (url.url) {
123141
article.body = article.body.replace(url.originalUrl, `![](${url.url})`);
124142
if (url.exists) {
125143
out.info(`图片已存在 skip: ${url.url}`);
126144
} else {
127145
out.info(`replace ${url.yuqueRealImgUrl} to ${url.url}`);
128146
}
147+
} else {
148+
out.warn(`图片替换失败,将使用原url: ${url.yuqueRealImgUrl}`);
129149
}
130150
});
131151
return article;

0 commit comments

Comments
 (0)