├── .prettierrc.json ├── success-log ├── 已存在, 跳过....txt ├── 秒传成功, 保存到网盘路径.txt ├── 上传文件成功, 保存到网盘路径.txt ├── meta 不存在.txt ├── meta 存在.txt └── panic runtime error index out of range.txt ├── error-log ├── 全部上传完毕, 总大小 0B.txt ├── 上传文件错误 上传状态过期, 请重新上传.txt ├── 登陆失败 网络错误, http 响应错误, 403 Forbidden.txt ├── file name is invalid.txt ├── http 响应错误, 400 Bad Request.txt ├── 未登录 代码 31045, 消息 操作失败,可能百度帐号登录状态 过期.txt ├── 远端服务器返回错误, 代码 31352, 消息 commit superfile2 failed.txt ├── meta 遇到错误, 未知返回数据.txt ├── json 数据解析失败.txt ├── 打开上传未完成数据库错误.txt ├── 网络错误, Get net http timeout awaiting response headers.txt ├── 网络错误, Post net http timeout awaiting response headers.txt ├── 网络错误, Get An existing connection was forcibly closed by the remote host.txt └── panic runtime error index out of range.txt ├── lib ├── cli.d.ts ├── argsobj.d.ts ├── checkmetamsg.d.ts ├── onFailedAttempt.d.ts ├── parse-args.js ├── parse-args.d.ts ├── start.d.ts ├── findfiles.d.ts ├── upload.d.ts ├── retryupload.d.ts ├── uploadandcheck.d.ts ├── handleup.d.ts ├── argsobj.js ├── argsobj.ts ├── limiter.d.ts ├── resolvefiledestination.d.ts ├── checkexist.d.ts ├── execmeta.js ├── execmeta.d.ts ├── execbaidupcs.js ├── execmeta.ts ├── execbaidupcs.d.ts ├── uploadfile.d.ts ├── retryupload.js ├── findfiles.js ├── execbaidupcs.ts ├── retryupload.ts ├── index.d.ts ├── resolvefiledestination.js ├── limiter.js ├── findfiles.ts ├── resolvefiledestination.ts ├── onFailedAttempt.js ├── onFailedAttempt.ts ├── checkmetamsg.js ├── limiter.ts ├── index.js ├── cli.js ├── uploadfile.js ├── index.ts ├── start.js ├── parse-args.ts ├── uploadfile.ts ├── handleup.js ├── start.ts ├── cli.ts ├── checkmetamsg.ts ├── handleup.ts ├── uploadandcheck.js ├── uploadandcheck.ts ├── checkexist.js ├── checkexist.ts ├── upload.js └── upload.ts ├── .gitattributes ├── help.txt ├── .github ├── dependabot.yml └── workflows │ └── codeql-analysis.yml ├── tsconfig.json ├── LICENSE ├── package.json ├── README.md └── .gitignore /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { "tabWidth": 4 } 2 | -------------------------------------------------------------------------------- /success-log/已存在, 跳过....txt: -------------------------------------------------------------------------------- 1 | 已存在, 跳过... 2 | -------------------------------------------------------------------------------- /error-log/全部上传完毕, 总大小 0B.txt: -------------------------------------------------------------------------------- 1 | 全部上传完毕, 总大小: 0B 2 | -------------------------------------------------------------------------------- /success-log/秒传成功, 保存到网盘路径.txt: -------------------------------------------------------------------------------- 1 | 秒传成功, 保存到网盘路径: 2 | -------------------------------------------------------------------------------- /lib/cli.d.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | export {}; 3 | -------------------------------------------------------------------------------- /success-log/上传文件成功, 保存到网盘路径.txt: -------------------------------------------------------------------------------- 1 | 上传文件成功, 保存到网盘路径: 2 | -------------------------------------------------------------------------------- /lib/argsobj.d.ts: -------------------------------------------------------------------------------- 1 | export declare const argsobj: any; 2 | -------------------------------------------------------------------------------- /error-log/上传文件错误 上传状态过期, 请重新上传.txt: -------------------------------------------------------------------------------- 1 | 2 | 上传文件错误: 上传状态过期, 请重新上传 3 | -------------------------------------------------------------------------------- /error-log/登陆失败 网络错误, http 响应错误, 403 Forbidden.txt: -------------------------------------------------------------------------------- 1 | 网络错误, http 响应错误, 403 Forbidden -------------------------------------------------------------------------------- /lib/checkmetamsg.d.ts: -------------------------------------------------------------------------------- 1 | export declare function checkmetamsg(stdout: string): boolean; 2 | -------------------------------------------------------------------------------- /error-log/file name is invalid.txt: -------------------------------------------------------------------------------- 1 | 遇到错误, 远端服务器返回错误, 代码: 31062, 消息: file name is invalid 2 | -------------------------------------------------------------------------------- /lib/onFailedAttempt.d.ts: -------------------------------------------------------------------------------- 1 | export declare function onFailedAttempt(e: Error): Promise; 2 | -------------------------------------------------------------------------------- /error-log/http 响应错误, 400 Bad Request.txt: -------------------------------------------------------------------------------- 1 | 上传文件失败, 分片上传—文件分片及上传: 网络错误, http 响应错误, 400 Bad Request 2 | -------------------------------------------------------------------------------- /lib/parse-args.js: -------------------------------------------------------------------------------- 1 | export { parseargs }; 2 | import parseargs from "@masx200/mini-cli-args-parser"; 3 | -------------------------------------------------------------------------------- /lib/parse-args.d.ts: -------------------------------------------------------------------------------- 1 | export { parseargs }; 2 | import parseargs from "@masx200/mini-cli-args-parser"; 3 | -------------------------------------------------------------------------------- /lib/start.d.ts: -------------------------------------------------------------------------------- 1 | export { start }; 2 | declare function start(input: string, dest: string): Promise; 3 | -------------------------------------------------------------------------------- /lib/findfiles.d.ts: -------------------------------------------------------------------------------- 1 | declare function findfiles(root: string): Promise; 2 | export default findfiles; 3 | -------------------------------------------------------------------------------- /lib/upload.d.ts: -------------------------------------------------------------------------------- 1 | export declare function upload( 2 | file: string, 3 | destination: string 4 | ): Promise; 5 | -------------------------------------------------------------------------------- /lib/retryupload.d.ts: -------------------------------------------------------------------------------- 1 | export declare function retryupload( 2 | file: string, 3 | destination: string 4 | ): Promise; 5 | -------------------------------------------------------------------------------- /error-log/未登录 代码 31045, 消息 操作失败,可能百度帐号登录状态 过期.txt: -------------------------------------------------------------------------------- 1 | 遇到错误, 远端服务器返回错误, 代码: 31045, 消息: 操作失败, 可能百度帐号登录状态过期, 请尝试重新登录, 消息: user not exists 2 | -------------------------------------------------------------------------------- /lib/uploadandcheck.d.ts: -------------------------------------------------------------------------------- 1 | export declare function uploadandcheck( 2 | file: string, 3 | destination: string 4 | ): Promise; 5 | -------------------------------------------------------------------------------- /error-log/ 远端服务器返回错误, 代码 31352, 消息 commit superfile2 failed.txt: -------------------------------------------------------------------------------- 1 | 2 | 上传文件失败, 分片上传—合并分片文件: 遇到错误, 远端服务器返回错误, 代码: 31352, 消息: commit superfile2 failed 3 | -------------------------------------------------------------------------------- /lib/handleup.d.ts: -------------------------------------------------------------------------------- 1 | export declare function handleup( 2 | filelist: string[], 3 | input: string, 4 | dest: string 5 | ): Promise; 6 | -------------------------------------------------------------------------------- /lib/argsobj.js: -------------------------------------------------------------------------------- 1 | import process from "process"; 2 | import { parseargs } from "./parse-args.js"; 3 | export const argsobj = parseargs(process.argv.slice(2)); 4 | -------------------------------------------------------------------------------- /lib/argsobj.ts: -------------------------------------------------------------------------------- 1 | import process from "process"; 2 | import { parseargs } from "./parse-args.js"; 3 | export const argsobj = parseargs(process.argv.slice(2)); 4 | -------------------------------------------------------------------------------- /lib/limiter.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { execFile } from "child_process"; 3 | export declare const limitexec: typeof execFile.__promisify__; 4 | -------------------------------------------------------------------------------- /lib/resolvefiledestination.d.ts: -------------------------------------------------------------------------------- 1 | export declare function resolvefiledestination( 2 | file: string, 3 | input: string, 4 | dest: string 5 | ): string; 6 | -------------------------------------------------------------------------------- /success-log/meta 不存在.txt: -------------------------------------------------------------------------------- 1 | $ BaiduPCS-Go meta /我的音乐-2020-08-05/司夏/司夏-明月顾我.mp3h 2 | [0] - [/我的音乐-2020-08-05/司夏/司夏-明月顾我.mp3h] -------------- 3 | 获取文件/目录的元信息: 遇到错误, 远端服务器返回错误, 代码: 31066, 消息: 文件或目录不存在 4 | -------------------------------------------------------------------------------- /error-log/meta 遇到错误, 未知返回数据.txt: -------------------------------------------------------------------------------- 1 | [0] - [/d0dcaa567d137e3f66735fbe8cfd3c38.webp] -------------- 2 | 3 | 4 | 获取文件/目录的元信息, 遇到错误, 未知返回数据 5 | -------------------------------------------------------------------------------- /error-log/ json 数据解析失败.txt: -------------------------------------------------------------------------------- 1 | 获取文件列表错误, 获取目录下的文件列表: json 数据解析失败, baidupcs.fdDataJSONExport.List: []*baidupcs.fdJSON: baidupcs.fdJSON.MD5: net/http: request canceled (Client.Timeout exceeded while reading body) 2 | -------------------------------------------------------------------------------- /lib/checkexist.d.ts: -------------------------------------------------------------------------------- 1 | import pupkg from "@shanyue/promise-utils"; 2 | export declare function checkexist(remotefile: string): Promise; 3 | declare const sleep: typeof pupkg.sleep; 4 | export { sleep }; 5 | -------------------------------------------------------------------------------- /lib/execmeta.js: -------------------------------------------------------------------------------- 1 | import { limitexec } from "./limiter.js"; 2 | import { cmd } from "./index.js"; 3 | export default function execmeta(remotefile) { 4 | return limitexec(cmd, ["meta", remotefile]); 5 | } 6 | -------------------------------------------------------------------------------- /lib/execmeta.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | export default function execmeta( 3 | remotefile: string 4 | ): import("child_process").PromiseWithChild<{ 5 | stdout: string; 6 | stderr: string; 7 | }>; 8 | -------------------------------------------------------------------------------- /lib/execbaidupcs.js: -------------------------------------------------------------------------------- 1 | import { limitexec } from "./limiter.js"; 2 | import { cmd } from "./index.js"; 3 | export default function execup(localfile, desdir) { 4 | return limitexec(cmd, ["upload", localfile, desdir]); 5 | } 6 | -------------------------------------------------------------------------------- /lib/execmeta.ts: -------------------------------------------------------------------------------- 1 | import { limitexec } from "./limiter.js"; 2 | 3 | import { cmd } from "./index.js"; 4 | 5 | export default function execmeta(remotefile: string) { 6 | return limitexec(cmd, ["meta", remotefile]); 7 | } 8 | -------------------------------------------------------------------------------- /lib/execbaidupcs.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | export default function execup( 3 | localfile: string, 4 | desdir: string 5 | ): import("child_process").PromiseWithChild<{ 6 | stdout: string; 7 | stderr: string; 8 | }>; 9 | -------------------------------------------------------------------------------- /lib/uploadfile.d.ts: -------------------------------------------------------------------------------- 1 | export declare const fatalerror: string[]; 2 | export declare const directfailure: string[]; 3 | export declare const retrymsg: string[]; 4 | export declare const successmsg: string[]; 5 | export declare const successerror: string[]; 6 | -------------------------------------------------------------------------------- /lib/retryupload.js: -------------------------------------------------------------------------------- 1 | import { uploadandcheck } from "./uploadandcheck.js"; 2 | export async function retryupload(file, destination) { 3 | return new Promise((res) => { 4 | setTimeout(() => { 5 | res(uploadandcheck(file, destination)); 6 | }, 5000); 7 | }); 8 | } 9 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | #改造成typescript 2 | # Auto detect text files and perform LF normalization 3 | #* text=auto 4 | *.ts linguist-language=Typescript 5 | *.js linguist-language=Typescript 6 | *.css linguist-language=Typescript 7 | *.html linguist-language=Typescript 8 | *.jsx linguist-language=Typescript 9 | -------------------------------------------------------------------------------- /lib/findfiles.js: -------------------------------------------------------------------------------- 1 | import find from "find"; 2 | function findfiles(root) { 3 | return new Promise((s, j) => { 4 | find.file(root, (files) => { 5 | s(files); 6 | }).error((e) => { 7 | j(e); 8 | }); 9 | }); 10 | } 11 | export default findfiles; 12 | -------------------------------------------------------------------------------- /lib/execbaidupcs.ts: -------------------------------------------------------------------------------- 1 | import { limitexec } from "./limiter.js"; 2 | import { cmd } from "./index.js"; 3 | 4 | /** 5 | * @param {string} localfile 6 | * @param {string} desdir 7 | */ 8 | export default function execup(localfile: string, desdir: string) { 9 | return limitexec(cmd, ["upload", localfile, desdir]); 10 | } 11 | -------------------------------------------------------------------------------- /lib/retryupload.ts: -------------------------------------------------------------------------------- 1 | import { uploadandcheck } from "./uploadandcheck.js"; 2 | 3 | export async function retryupload( 4 | file: string, 5 | destination: string 6 | ): Promise { 7 | return new Promise((res) => { 8 | setTimeout(() => { 9 | res(uploadandcheck(file, destination)); 10 | }, 5000); 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /error-log/打开上传未完成数据库错误.txt: -------------------------------------------------------------------------------- 1 | 打开上传未完成数据库错误: 2 | pcsupload.UploadingDatabase.UploadingList: []*pcsupload.Uploading: pcsupload.Uploading.isObjectEnd: object ended prematurely, unexpected char i, error found in #1 byte of ...|icemd5":"vC|..., bigger context ...|icemd5":"vCMkhbgnxAwEmJsqNQIW7g==","md5":"vCMkhbgnx|... 3 | -------------------------------------------------------------------------------- /lib/index.d.ts: -------------------------------------------------------------------------------- 1 | export declare const slicecount = 500; 2 | import { start } from "./start.js"; 3 | export declare const cmd: string; 4 | export declare let 总数: { 5 | value: number; 6 | toString(): number; 7 | valueOf(): number; 8 | }; 9 | export { start }; 10 | export declare let 完成数: { 11 | value: number; 12 | toString(): number; 13 | valueOf(): number; 14 | }; 15 | -------------------------------------------------------------------------------- /lib/resolvefiledestination.js: -------------------------------------------------------------------------------- 1 | import path, { posix } from "path"; 2 | export function resolvefiledestination(file, input, dest) { 3 | const inputbase = path.basename(input); 4 | const destination = posix.dirname( 5 | posix 6 | .resolve(dest, inputbase, path.relative(input, file)) 7 | .replace(/\\/g, "/") 8 | ); 9 | return destination; 10 | } 11 | -------------------------------------------------------------------------------- /lib/limiter.js: -------------------------------------------------------------------------------- 1 | import AsyncLimiter from "@masx200/async-task-current-limiter"; 2 | import { execFile } from "child_process"; 3 | import { promisify } from "util"; 4 | import { argsobj } from "./argsobj.js"; 5 | const 同时并发的上传文件个数 = Number(argsobj["concurrent"]) || 15; 6 | const limiter = AsyncLimiter(同时并发的上传文件个数); 7 | const execpro = promisify(execFile); 8 | export const limitexec = limiter.asyncwrap(execpro); 9 | -------------------------------------------------------------------------------- /lib/findfiles.ts: -------------------------------------------------------------------------------- 1 | import find from "find"; 2 | /** 3 | * 4 | * @param {string} root 5 | * @returns{Promise} 6 | */ 7 | function findfiles(root: string): Promise { 8 | return new Promise((s, j) => { 9 | find.file(root, (files) => { 10 | s(files); 11 | }).error((e) => { 12 | j(e); 13 | }); 14 | }); 15 | } 16 | export default findfiles; 17 | -------------------------------------------------------------------------------- /help.txt: -------------------------------------------------------------------------------- 1 | 2 | 使用baidupcs-go,百度网盘批量上传图片 3 | 4 | 必选参数 `input`:类型`string`,输入本地文件目录 5 | 6 | 必选参数 `dest`:类型`string`,输出网盘文件目录 7 | 8 | 可选参数`concurrent`:类型`number`,同时并发的上传文件个数 9 | 10 | 示例: 11 | 12 | node ./cli.js --input=D:/baidupandownload/图片输入本地 --dest=/baidupandownload/图片输出网盘 13 | 14 | npx @masx200/baidupcs-batch-upload --input=D:/baidupandownload/图片输入本地 --dest=/baidupandownload/图片输出网盘 --concurrent=20 15 | 16 | 输入的参数有误! -------------------------------------------------------------------------------- /success-log/meta 存在.txt: -------------------------------------------------------------------------------- 1 | BaiduPCS-Go meta /我的音乐-2020-08-05/司夏/司夏-明月顾我.mp3 2 | [0] - [/我的音乐-2020-08-05/司夏/司夏-明月顾我.mp3] 3 | -------------- 4 | 类型 5 | 文件 6 | 文件路径 7 | /我的音乐-2020-08-05/司夏/司夏-明月顾我.mp3 8 | 文件名称 9 | 司夏-明月顾我.mp3 10 | 文件大小 11 | 4827122, 4.603502MB 12 | md5 (截图请打码) 13 | e5543b4a37606e6e80f29a1ca03073b7 14 | app_id 15 | 266719 16 | fs_id 17 | 196729648356829 18 | 创建日期 19 | 2020-08-05 23:28:51 20 | 修改日期 21 | 2020-08-05 23:28:51 22 | -------------------------------------------------------------------------------- /lib/resolvefiledestination.ts: -------------------------------------------------------------------------------- 1 | import path, { posix } from "path"; 2 | 3 | export function resolvefiledestination( 4 | file: string, 5 | input: string, 6 | dest: string 7 | ) { 8 | const inputbase = path.basename(input); 9 | const destination = posix.dirname( 10 | posix 11 | .resolve(dest, inputbase, path.relative(input, file)) 12 | .replace(/\\/g, "/") 13 | ); 14 | return destination; 15 | } 16 | -------------------------------------------------------------------------------- /lib/onFailedAttempt.js: -------------------------------------------------------------------------------- 1 | import { sleep } from "./checkexist.js"; 2 | import { fatalerror } from "./uploadfile.js"; 3 | export async function onFailedAttempt(e) { 4 | console.warn(e); 5 | const stdout = Reflect.has(e, "stdout") && Reflect.get(e, "stdout"); 6 | if (typeof stdout !== "string") { 7 | throw e; 8 | } 9 | if (fatalerror.some((m) => stdout.includes(m))) { 10 | throw e; 11 | } 12 | console.warn("运行命令查询错误,4秒后重试"); 13 | await sleep(4000); 14 | } 15 | -------------------------------------------------------------------------------- /lib/onFailedAttempt.ts: -------------------------------------------------------------------------------- 1 | import { sleep } from "./checkexist.js"; 2 | import { fatalerror } from "./uploadfile.js"; 3 | 4 | export async function onFailedAttempt(e: Error) { 5 | console.warn(e); 6 | 7 | const stdout = Reflect.has(e, "stdout") && Reflect.get(e, "stdout"); 8 | 9 | if (typeof stdout !== "string") { 10 | throw e; 11 | } 12 | if (fatalerror.some((m) => stdout.includes(m))) { 13 | throw e; 14 | } 15 | 16 | console.warn("运行命令查询错误,4秒后重试"); 17 | await sleep(4000); 18 | } 19 | -------------------------------------------------------------------------------- /error-log/网络错误, Get net http timeout awaiting response headers.txt: -------------------------------------------------------------------------------- 1 | 获取文件列表错误, 获取目录下的文件列表: 网络错误, Get http://pcs.baidu.com/rest/2.0/pcs/file?app_id=266719&by=name&limit=0-2147483647&method=list&order=asc&path=%2F%E6%88%91%E7%9A%84%E5%9B%BE%E7%89%87%2F%E5%BE%AE%E5%8D%9A%E7%BE%8E%E5%9B%BE%E5%90%88%E9%9B%86-2020-02-15%2B2020-02-14%2F%E5%9B%BE%E7%89%87%E5%8E%8B%E7%BC%A9%E8%BE%93%E5%87%BA%2F%E5%BE%AE%E5%8D%9A%E7%BE%8E%E5%9B%BE%E5%90%88%E9%9B%86-2020-02-14%2FMomoko%E8%91%B5%E8%91%B5%2F%E5%BE%AE%E5%8D%9A%E9%85%8D%E5%9B%BE: net/http: timeout awaiting response headers 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /error-log/网络错误, Post net http timeout awaiting response headers.txt: -------------------------------------------------------------------------------- 1 | 上传文件失败, 分片上传—合并分片文件: 网络错误, Post http://pcs.baidu.com/rest/2.0/pcs/file?app_id=266719&method=createsuperfile&ondup=newcopy&path=%2F%E6%88%91%E7%9A%84%E5%9B%BE%E7%89%87%2F%E5%BE%AE%E5%8D%9A%E7%BE%8E%E5%9B%BE%E5%90%88%E9%9B%86-2020-02-15%2B2020-02-14%2F%E6%9A%B4%E5%8A%9B%E5%88%87%E5%89%B2%E5%9B%BE%E7%89%87%2F%E5%BE%AE%E5%8D%9A%E7%BE%8E%E5%9B%BE%E5%90%88%E9%9B%86-2020-02-14%2F%E6%95%99%E4%B8%BBShadow%2F%E5%BE%AE%E5%8D%9A%E9%85%8D%E5%9B%BE%2Fcea38c192fb82c89b706e810aa37ea9a.webp: net/http: timeout awaiting response headers 2 | -------------------------------------------------------------------------------- /lib/checkmetamsg.js: -------------------------------------------------------------------------------- 1 | export function checkmetamsg(stdout) { 2 | const infoarr = stdout 3 | .split("--------------")?.[1] 4 | ?.split(/\s+/) 5 | ?.filter(Boolean); 6 | if (!infoarr) { 7 | return false; 8 | } 9 | if (infoarr?.indexOf("文件大小") < 0) { 10 | return false; 11 | } 12 | const sizenotzero = !infoarr?.[infoarr.indexOf("文件大小") + 1]?.startsWith( 13 | "0" 14 | ); 15 | return !!( 16 | infoarr?.[0] === "类型" && 17 | infoarr?.[1] === "文件" && 18 | sizenotzero 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /lib/limiter.ts: -------------------------------------------------------------------------------- 1 | import AsyncLimiter from "@masx200/async-task-current-limiter"; 2 | import { execFile } from "child_process"; 3 | import { promisify } from "util"; 4 | import { argsobj } from "./argsobj.js"; 5 | const 同时并发的上传文件个数 = Number(argsobj["concurrent"]) || 15; 6 | const limiter = AsyncLimiter(同时并发的上传文件个数); 7 | /** 8 | * @param {any} data 9 | */ 10 | // const listener = (data: any) => console.log("限流状态" + JSON.stringify(data)); 11 | // limiter.target.on("free", listener); 12 | // limiter.target.on("full", listener); 13 | const execpro = promisify(execFile); 14 | 15 | export const limitexec = limiter.asyncwrap(execpro); 16 | -------------------------------------------------------------------------------- /error-log/网络错误, Get An existing connection was forcibly closed by the remote host.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 获取文件列表错误, 获取目录下的文件列表: 网络错误, Get http://pcs.baidu.com/rest/2.0/pcs/file?app_id=266719&by=name&limit=0-2147483647&method=list&order=asc&path=%2F%E6%88%91%E7%9A%84%E5%9B%BE%E7%89%87%2F%E5%BE%AE%E5%8D%9A%E7%BE%8E%E5%9B%BE%E5%90%88%E9%9B%86-2020-02-15%2B2020-02-14%2F%E5%9B%BE%E7%89%87%E5%8E%8B%E7%BC%A9%E8%BE%93%E5%87%BA%2F%E5%BE%AE%E5%8D%9A%E7%BE%8E%E5%9B%BE%E5%90%88%E9%9B%86-2020-02-14%2FFin_%E9%99%88%E7%95%99%E7%99%BD%2F%E5%BE%AE%E5%8D%9A%E9%85%8D%E5%9B%BE: read tcp 192.168.31.226:2110->140.249.34.53:80: wsarecv: An existing connection was forcibly closed by the remote host. 4 | 5 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | export const slicecount = 500; 2 | import process from "process"; 3 | import os from "os"; 4 | import { start } from "./start.js"; 5 | export const cmd = 6 | "win32" === os.platform() ? "BaiduPCS-Go.exe" : "BaiduPCS-Go"; 7 | process.on("unhandledRejection", (e) => { 8 | throw e; 9 | }); 10 | export let 总数 = { 11 | value: 0, 12 | toString() { 13 | return this.value; 14 | }, 15 | valueOf() { 16 | return this.value; 17 | }, 18 | }; 19 | export { start }; 20 | export let 完成数 = { 21 | value: 0, 22 | toString() { 23 | return this.value; 24 | }, 25 | valueOf() { 26 | return this.value; 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /lib/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import fs from "fs"; 3 | import path, { dirname } from "path"; 4 | import process from "process"; 5 | import { fileURLToPath } from "url"; 6 | import { argsobj } from "./argsobj.js"; 7 | import { start } from "./start.js"; 8 | const { input, dest, concurrent } = argsobj; 9 | const __filename = fileURLToPath(import.meta.url); 10 | const __dirname = dirname(__filename); 11 | const helppath = path.join(__dirname, "../help.txt"); 12 | const helptxt = String(fs.readFileSync(helppath)); 13 | console.log(argsobj); 14 | if (input && dest) { 15 | console.log({ 16 | input, 17 | dest, 18 | concurrent, 19 | }); 20 | start(input, dest); 21 | } else { 22 | console.error(helptxt); 23 | process.exit(1); 24 | } 25 | -------------------------------------------------------------------------------- /lib/uploadfile.js: -------------------------------------------------------------------------------- 1 | export const fatalerror = [ 2 | "遇到错误, 远端服务器返回错误, 代码: 31062, 消息: file name is invalid", 3 | "遇到错误, 远端服务器返回错误, 代码: 31045, 消息: 操作失败, 可能百度帐号登录状态过期", 4 | "网络错误, http 响应错误, 403 Forbidden", 5 | ]; 6 | export const directfailure = [ 7 | "以下文件上传失败:", 8 | "全部上传完毕, 总大小: 0B", 9 | "打开上传未完成数据库错误:", 10 | ]; 11 | export const retrymsg = [ 12 | "网络错误, http 响应错误,", 13 | "遇到错误, 远端服务器返回错误, 代码: 31352, 消息: commit superfile2 failed", 14 | "网络错误, Post", 15 | "json 数据解析失败,", 16 | "获取文件列表错误, 获取目录下的文件列表", 17 | "网络错误, Get", 18 | "上传文件错误: 上传状态过期, 请重新上传", 19 | "上传文件失败, 分片上传—合并分片文件", 20 | ]; 21 | export const successmsg = [ 22 | ", 已存在, 跳过...", 23 | "秒传成功, 保存到网盘路径:", 24 | "上传文件成功, 保存到网盘路径:", 25 | ]; 26 | export const successerror = ["panic: runtime error: index out of range"]; 27 | -------------------------------------------------------------------------------- /lib/index.ts: -------------------------------------------------------------------------------- 1 | export const slicecount = 500; 2 | 3 | // const input = `D:/baidupandownload/微博美图相册-2020-02-13`; 4 | // const dest = `/!我的图片-2020-02-10/微博美图相册-2020-02-13`; 5 | import process from "process"; 6 | import os from "os"; 7 | import { start } from "./start.js"; 8 | export const cmd = 9 | "win32" === os.platform() ? "BaiduPCS-Go.exe" : "BaiduPCS-Go"; 10 | process.on("unhandledRejection", (e) => { 11 | throw e; 12 | }); 13 | export let 总数 = { 14 | value: 0, 15 | toString() { 16 | return this.value; 17 | }, 18 | valueOf() { 19 | return this.value; 20 | }, 21 | }; //0; 22 | //export let 完成数 ={value:0}// 0; 23 | 24 | export { start }; 25 | 26 | export let 完成数 = { 27 | value: 0, 28 | toString() { 29 | return this.value; 30 | }, 31 | valueOf() { 32 | return this.value; 33 | }, 34 | }; //0; 35 | -------------------------------------------------------------------------------- /lib/start.js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | import findfile from "./findfiles.js"; 4 | import { handleup } from "./handleup.js"; 5 | import { 总数 } from "./index.js"; 6 | export { start }; 7 | async function start(input, dest) { 8 | const filedatas = await findfile(path.resolve(input)); 9 | filedatas.sort(); 10 | console.log("找到文件" + filedatas.length + "个"); 11 | console.log(JSON.stringify(filedatas, null, 4)); 12 | const filesizes = await Promise.all( 13 | filedatas.map(async (file) => { 14 | const stat = await fs.promises.stat(file); 15 | return stat.size; 16 | }) 17 | ); 18 | const filelist = filedatas.filter((file, index) => { 19 | return filesizes[index]; 20 | }); 21 | 总数.value = filelist.length; 22 | await handleup(filelist, input, dest); 23 | console.log("全部处理完成"); 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "exclude": [], 3 | "include": ["./**/*.ts"], 4 | "compilerOptions": { 5 | "pretty": true, 6 | "removeComments": true, 7 | "moduleResolution": "node", 8 | "resolveJsonModule": false, 9 | "declaration": true, 10 | "allowJs": true, 11 | "checkJs": true, 12 | "charset": "utf-8", 13 | "strict": true, 14 | "target": "ESNEXT", 15 | "module": "ESNEXT", 16 | "importHelpers": true, 17 | "noImplicitAny": true, 18 | "strictNullChecks": true, 19 | "noImplicitThis": true, 20 | "alwaysStrict": true, 21 | "noUnusedLocals": true, 22 | "noImplicitReturns": true, 23 | "noFallthroughCasesInSwitch": true, 24 | "esModuleInterop": true, 25 | "allowSyntheticDefaultImports": true, 26 | "forceConsistentCasingInFileNames": true 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/parse-args.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string[]} args 3 | * @returns {Record} 4 | */ 5 | // function parseargs(args: string[]): Record { 6 | // /** 7 | // * @type{Record} 8 | // */ 9 | // const 参数obj: Record = {}; 10 | // args.filter((s) => s.startsWith("--")) 11 | // .map((s) => /--(?.+)=(?.+)/g.exec(s)) 12 | // .forEach((execArray) => { 13 | // var _a, _b, _c; 14 | // const groups = 15 | // (_a = execArray) === null || _a === void 0 ? void 0 : _a.groups; 16 | // const key = 17 | // (_b = groups) === null || _b === void 0 ? void 0 : _b.key; 18 | // const value = 19 | // (_c = groups) === null || _c === void 0 ? void 0 : _c.value; 20 | // if (key && value) { 21 | // 参数obj[key] = value; 22 | // } 23 | // }); 24 | // return 参数obj; 25 | // } 26 | export { parseargs }; 27 | import parseargs from "@masx200/mini-cli-args-parser"; 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 masx200 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/uploadfile.ts: -------------------------------------------------------------------------------- 1 | export const fatalerror = [ 2 | "遇到错误, 远端服务器返回错误, 代码: 31062, 消息: file name is invalid", 3 | "遇到错误, 远端服务器返回错误, 代码: 31045, 消息: 操作失败, 可能百度帐号登录状态过期", 4 | "网络错误, http 响应错误, 403 Forbidden", 5 | ]; 6 | //遇到错误, 远端服务器返回错误, 代码: 31045, 消息: 操作失败, 可能百度帐号登录状态过期, 请尝试重新登录, 消息: user not exists 7 | //export { fatalerror }; 8 | export const directfailure = [ 9 | "以下文件上传失败:", 10 | 11 | "全部上传完毕, 总大小: 0B", 12 | // "ms, 总大小: 0B", 13 | "打开上传未完成数据库错误:", 14 | ]; 15 | /* 16 | 17 | [1] 加入上传队列: /sdcard/test/test/文件.txt 18 | [1] 准备上传: /sdcard/test/test/文件.txt 19 | [1] 目标文件, /我的测试/test/文件.txt, 已存在, 跳过... 20 | 21 | 上传结束, 时间: 547ms, 总大小: 0B 22 | 23 | */ 24 | export const retrymsg = [ 25 | "网络错误, http 响应错误,", 26 | "遇到错误, 远端服务器返回错误, 代码: 31352, 消息: commit superfile2 failed", 27 | "网络错误, Post", 28 | "json 数据解析失败,", 29 | "获取文件列表错误, 获取目录下的文件列表", 30 | "网络错误, Get", 31 | "上传文件错误: 上传状态过期, 请重新上传", 32 | "上传文件失败, 分片上传—合并分片文件", 33 | ]; 34 | export const successmsg = [ 35 | ", 已存在, 跳过...", 36 | "秒传成功, 保存到网盘路径:", 37 | "上传文件成功, 保存到网盘路径:", 38 | ]; 39 | export const successerror = ["panic: runtime error: index out of range"]; 40 | -------------------------------------------------------------------------------- /lib/handleup.js: -------------------------------------------------------------------------------- 1 | import process from "process"; 2 | import { uploadandcheck } from "./uploadandcheck.js"; 3 | import { resolvefiledestination } from "./resolvefiledestination.js"; 4 | import { slicecount, 完成数, 总数 } from "./index.js"; 5 | export async function handleup(filelist, input, dest) { 6 | const files = filelist; 7 | if (!files.length) { 8 | return; 9 | } else if (files.length > slicecount) { 10 | const workfiles = files.slice(0, slicecount); 11 | const restfiles = files.slice(slicecount); 12 | await handleup(workfiles, input, dest); 13 | await handleup(restfiles, input, dest); 14 | return; 15 | } else { 16 | await Promise.all( 17 | filelist.map(async (file) => { 18 | const destination = resolvefiledestination(file, input, dest); 19 | await uploadandcheck(file, destination); 20 | 完成数.value++; 21 | const 进度 = 22 | "完成进度:" + 23 | `${(完成数.value / 总数.value) * 100} % ` + 24 | `${完成数.toString()} / ${总数.valueOf()}`; 25 | console.log(进度); 26 | process.title = 进度; 27 | }) 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/start.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | import findfile from "./findfiles.js"; 4 | import { handleup } from "./handleup.js"; 5 | import { 总数 } from "./index.js"; 6 | export { start }; 7 | 8 | /** 9 | 10 | *@param {string} input 11 | *@param {string} dest 12 | * */ 13 | 14 | async function start( 15 | input: string, 16 | dest: string 17 | /*, reverse = false*/ 18 | ) { 19 | const filedatas = await findfile(path.resolve(input)); 20 | //文件排序一下 21 | filedatas.sort(); 22 | // 总数 = filedatas.length; 23 | console.log("找到文件" + filedatas.length + "个"); 24 | console.log(JSON.stringify(filedatas, null, 4)); 25 | 26 | /* 要把文件大小为0的文件排除,否则上传失败 */ 27 | const filesizes = await Promise.all( 28 | filedatas.map(async (file) => { 29 | const stat = await fs.promises.stat(file); 30 | return stat.size; 31 | }) 32 | ); 33 | const filelist = filedatas.filter((file, index) => { 34 | return filesizes[index]; 35 | }); 36 | 37 | 总数.value = filelist.length; 38 | 39 | //TODO 上传完大量文件之后会极少的一些文件返回信息上传成功,但实际上上传没有成功! 40 | //https://github.com/felixonmars/BaiduPCS-Go/issues/20 41 | //可以使用meta命令来判断文件是否存在 42 | await handleup(filelist, input, dest); 43 | console.log("全部处理完成"); 44 | } 45 | -------------------------------------------------------------------------------- /lib/cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 添加反向的参数可选 */ 3 | //const reverse = !!argsobj["reverse"]; 4 | import fs from "fs"; 5 | import path, { dirname } from "path"; 6 | import process from "process"; 7 | import { fileURLToPath } from "url"; 8 | import { argsobj } from "./argsobj.js"; 9 | import { start } from "./start.js"; 10 | const { input, dest, concurrent } = argsobj; 11 | 12 | const __filename = fileURLToPath(import.meta.url); 13 | const __dirname = dirname(__filename); 14 | const helppath = path.join(__dirname, "../help.txt"); 15 | const helptxt = String(fs.readFileSync(helppath)); 16 | console.log(argsobj); 17 | if (input && dest) { 18 | console.log({ 19 | input, 20 | dest /*, reverse */, 21 | 22 | concurrent, 23 | }); 24 | start(input, dest /*, reverse*/); 25 | } else { 26 | console.error(helptxt); 27 | // console.error("使用baidupcs-go,百度网盘批量上传图片"); 28 | // console.error("示例:"); 29 | // console.error( 30 | // `node ./cli.js --input=D:/baidupandownload/图片输入本地 --dest=/baidupandownload/图片输出网盘` 31 | // ); 32 | // console.error( 33 | // `npx @masx200/baidupcs-batch-upload --input=D:/baidupandownload/图片输入本地 --dest=/baidupandownload/图片输出网盘 --concurrent=20` 34 | // ); 35 | // console.error("输入的参数有误!"); 36 | process.exit(1); 37 | } 38 | -------------------------------------------------------------------------------- /lib/checkmetamsg.ts: -------------------------------------------------------------------------------- 1 | export function checkmetamsg(stdout: string): boolean { 2 | const infoarr = stdout 3 | .split("--------------")?.[1] 4 | ?.split(/\s+/) 5 | ?.filter(Boolean); 6 | if (!infoarr) { 7 | return false; 8 | } 9 | 10 | if (infoarr?.indexOf("文件大小") < 0) { 11 | return false; 12 | } 13 | const sizenotzero = !infoarr?.[infoarr.indexOf("文件大小") + 1]?.startsWith( 14 | "0" 15 | ); 16 | return !!( 17 | infoarr?.[0] === "类型" && 18 | infoarr?.[1] === "文件" && 19 | sizenotzero 20 | ); 21 | } 22 | /* 23 | [ 24 | '类型', 25 | '文件', 26 | '文件路径', 27 | '/装机软件/QQLight_7.9.14308.0.exe', 28 | '文件名称', 29 | 'QQLight_7.9.14308.0.exe', 30 | '文件大小', 31 | '48842424,', 32 | '46.579765MB', 33 | 'md5', 34 | '(截图请打码)', 35 | '1ccc5df82e68c2f5028379dec0a047c1', 36 | 'app_id', 37 | '266719', 38 | 'fs_id', 39 | '173360764722406', 40 | '创建日期', 41 | '2016-10-24', 42 | '23:25:13', 43 | '修改日期', 44 | '2016-10-24', 45 | '23:25:13' 46 | ] 47 | 48 | 注:分享链接转存功能无法使用,可使用-cookies参数重新登录以启用 49 | [0] - [/装机软件/QQLight_7.9.14308.0.exe] -------------- 50 | 51 | 类型 文件 52 | 文件路径 /装机软件/QQLight_7.9.14308.0.exe 53 | 文件名称 QQLight_7.9.14308.0.exe 54 | 文件大小 48842424, 46.579765MB 55 | md5 (截图请打码) 1ccc5df82e68c2f5028379dec0a047c1 56 | app_id 266719 57 | fs_id 173360764722406 58 | 创建日期 2016-10-24 23:25:13 59 | 修改日期 2016-10-24 23:25:13 60 | */ 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "publishConfig": { 3 | "registry": "https://registry.npmjs.org" 4 | }, 5 | "name": "@masx200/baidupcs-batch-upload", 6 | "bin": "./lib/cli.js", 7 | "main": "./lib/index.js", 8 | "version": "1.3.2", 9 | "types": "./lib/index.d.ts", 10 | "module": "./lib/index.js", 11 | "type": "module", 12 | "scripts": { 13 | "build": "tsc", 14 | "prettier": "prettier **/*.ts **/*.js --write *.json *.md", 15 | "start": "node --experimental-modules --experimental-json-modules ./lib/cli.js " 16 | }, 17 | "dependencies": { 18 | "@masx200/async-task-current-limiter": "^2.1.0", 19 | "@masx200/mini-cli-args-parser": "^1.0.5", 20 | "@shanyue/promise-utils": "^2.0.4", 21 | "find": "^0.3.0", 22 | "fs-extra": "^11.1.1" 23 | }, 24 | "license": "MIT", 25 | "devDependencies": { 26 | "@types/find": "^0.2.1", 27 | "@types/fs-extra": "^11.0.1", 28 | "@types/node": "^18.0.0", 29 | "prettier": "^2.1.2", 30 | "tslib": "^2.0.3", 31 | "typescript": "^5.0.3" 32 | }, 33 | "keywords": [], 34 | "author": "masx200 <34191203+masx200@users.noreply.github.com>", 35 | "description": "使用baidupcs-go,百度网盘批量上传文件夹中的文件", 36 | "repository": "git+https://github.com/masx200/baidupcs-batch-upload.git", 37 | "bugs": { 38 | "url": "https://github.com/masx200/baidupcs-batch-upload/issues" 39 | }, 40 | "homepage": "https://github.com/masx200/baidupcs-batch-upload#readme" 41 | } 42 | -------------------------------------------------------------------------------- /lib/handleup.ts: -------------------------------------------------------------------------------- 1 | import process from "process"; 2 | import { uploadandcheck } from "./uploadandcheck.js"; 3 | import { resolvefiledestination } from "./resolvefiledestination.js"; 4 | import { slicecount, 完成数, 总数 } from "./index.js"; 5 | 6 | export async function handleup( 7 | filelist: string[], 8 | input: string, 9 | dest: string 10 | ) { 11 | const files = filelist; 12 | if (!files.length) { 13 | return; 14 | } else if (files.length > slicecount) { 15 | const workfiles = files.slice(0, slicecount); 16 | const restfiles = files.slice(slicecount); 17 | await handleup(workfiles, input, dest); 18 | await handleup(restfiles, input, dest); 19 | return; 20 | } else { 21 | await Promise.all( 22 | filelist.map( 23 | /** 24 | * @param {string} file 25 | */ 26 | async (file) => { 27 | // 给上传目标文件夹添加了输入文件夹的名字 28 | /* 29 | */ 30 | const destination = resolvefiledestination( 31 | file, 32 | input, 33 | dest 34 | ); 35 | 36 | await uploadandcheck(file, destination); 37 | 完成数.value++; 38 | const 进度 = 39 | "完成进度:" + 40 | `${(完成数.value / 总数.value) * 100} % ` + 41 | `${完成数.toString()} / ${总数.valueOf()}`; 42 | console.log(进度); 43 | process.title = 进度; 44 | } 45 | ) 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/uploadandcheck.js: -------------------------------------------------------------------------------- 1 | import { checkexist } from "./checkexist.js"; 2 | import path, { posix } from "path"; 3 | import { upload } from "./upload.js"; 4 | import { retryupload } from "./retryupload.js"; 5 | export async function uploadandcheck(file, destination) { 6 | const inputbase = path.basename(file); 7 | const remotefile = posix.join(destination, inputbase); 8 | if (Math.random() > 0.5) { 9 | const starttime = Date.now(); 10 | const fileexist = await checkexist(remotefile); 11 | if (fileexist) { 12 | console.log( 13 | ["检查网盘中存在此文件,跳过上传成功:", file, remotefile].join( 14 | "\n" 15 | ) 16 | ); 17 | const endtime = Date.now(); 18 | const durtime = (endtime - starttime) / 1000; 19 | console.log("用时" + durtime + "秒"); 20 | return; 21 | } else { 22 | console.warn( 23 | [ 24 | "检查网盘中不存在此文件,开始上传文件:", 25 | file, 26 | remotefile, 27 | ].join("\n") 28 | ); 29 | } 30 | } 31 | await upload(file, destination); 32 | const starttime = Date.now(); 33 | const fileexist = await checkexist(remotefile); 34 | if (fileexist) { 35 | console.log( 36 | ["检查网盘中存在此文件,上传文件成功:", file, remotefile].join( 37 | "\n" 38 | ) 39 | ); 40 | const endtime = Date.now(); 41 | const durtime = (endtime - starttime) / 1000; 42 | console.log("用时" + durtime + "秒"); 43 | return; 44 | } else { 45 | console.warn( 46 | ["检查网盘中不存在此文件,重新上传文件:", file, remotefile].join( 47 | "\n" 48 | ) 49 | ); 50 | return await retryupload(file, destination); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [master, ] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [master] 9 | schedule: 10 | - cron: '0 15 * * 3' 11 | 12 | jobs: 13 | analyse: 14 | name: Analyse 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v2 20 | with: 21 | # We must fetch at least the immediate parents so that if this is 22 | # a pull request then we can checkout the head. 23 | fetch-depth: 2 24 | 25 | # If this run was triggered by a pull request event, then checkout 26 | # the head of the pull request instead of the merge commit. 27 | - run: git checkout HEAD^2 28 | if: ${{ github.event_name == 'pull_request' }} 29 | 30 | # Initializes the CodeQL tools for scanning. 31 | - name: Initialize CodeQL 32 | uses: github/codeql-action/init@v1 33 | # Override language selection by uncommenting this and choosing your languages 34 | # with: 35 | # languages: go, javascript, csharp, python, cpp, java 36 | 37 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 38 | # If this step fails, then you should remove it and run the build manually (see below) 39 | - name: Autobuild 40 | uses: github/codeql-action/autobuild@v1 41 | 42 | # ℹ️ Command-line programs to run using the OS shell. 43 | # 📚 https://git.io/JvXDl 44 | 45 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 46 | # and modify them (or add more) to build your code if your project 47 | # uses a compiled language 48 | 49 | #- run: | 50 | # make bootstrap 51 | # make release 52 | 53 | - name: Perform CodeQL Analysis 54 | uses: github/codeql-action/analyze@v1 55 | -------------------------------------------------------------------------------- /lib/uploadandcheck.ts: -------------------------------------------------------------------------------- 1 | import { checkexist } from "./checkexist.js"; 2 | import path, { posix } from "path"; 3 | import { upload } from "./upload.js"; 4 | import { retryupload } from "./retryupload.js"; 5 | 6 | export async function uploadandcheck( 7 | file: string, 8 | destination: string 9 | ): Promise { 10 | const inputbase = path.basename(file); 11 | const remotefile = posix.join(destination, inputbase); 12 | 13 | if (Math.random() > 0.5) { 14 | const starttime = Date.now(); 15 | const fileexist = await checkexist(remotefile); 16 | 17 | if (fileexist) { 18 | console.log( 19 | ["检查网盘中存在此文件,跳过上传成功:", file, remotefile].join( 20 | "\n" 21 | ) 22 | ); 23 | const endtime = Date.now(); 24 | const durtime = (endtime - starttime) / 1000; 25 | console.log("用时" + durtime + "秒"); 26 | return; 27 | } else { 28 | console.warn( 29 | [ 30 | "检查网盘中不存在此文件,开始上传文件:", 31 | file, 32 | remotefile, 33 | ].join("\n") 34 | ); 35 | } 36 | } 37 | await upload(file, destination); 38 | 39 | const starttime = Date.now(); 40 | const fileexist = await checkexist(remotefile); 41 | if (fileexist) { 42 | console.log( 43 | ["检查网盘中存在此文件,上传文件成功:", file, remotefile].join( 44 | "\n" 45 | ) 46 | ); 47 | const endtime = Date.now(); 48 | const durtime = (endtime - starttime) / 1000; 49 | console.log("用时" + durtime + "秒"); 50 | return; 51 | } else { 52 | console.warn( 53 | ["检查网盘中不存在此文件,重新上传文件:", file, remotefile].join( 54 | "\n" 55 | ) 56 | ); 57 | return await retryupload(file, destination); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/checkexist.js: -------------------------------------------------------------------------------- 1 | import pupkg from "@shanyue/promise-utils"; 2 | import { checkmetamsg } from "./checkmetamsg.js"; 3 | import execmeta from "./execmeta.js"; 4 | import { onFailedAttempt } from "./onFailedAttempt.js"; 5 | export async function checkexist(remotefile) { 6 | const fileexist = await retry( 7 | async () => { 8 | const result = await execmeta(remotefile); 9 | const { stdout, stderr } = result; 10 | if (stdout === "" && stderr === "") { 11 | const e = Object.assign( 12 | new Error( 13 | "exec command failure! baidupcs-go:" + 14 | "\n" + 15 | stdout + 16 | "\n" + 17 | stderr 18 | ), 19 | { stdout, stderr } 20 | ); 21 | console.error(e); 22 | console.warn("运行命令查询错误,5秒后重试"); 23 | await sleep(5000); 24 | return await checkexist(remotefile); 25 | } 26 | if (stdout.includes(notexistmsg)) { 27 | return false; 28 | } else if (checkmetamsg(stdout)) { 29 | return true; 30 | } else if (!checkmetamsg(stdout)) { 31 | return false; 32 | } else { 33 | throw Object.assign( 34 | new Error( 35 | "exec command failure! baidupcs-go:" + 36 | "\n" + 37 | stdout + 38 | "\n" + 39 | stderr 40 | ), 41 | { stdout, stderr } 42 | ); 43 | } 44 | }, 45 | { 46 | times: 15, 47 | onFailedAttempt: onFailedAttempt, 48 | } 49 | ); 50 | return fileexist; 51 | } 52 | const { retry, sleep } = pupkg; 53 | const notexistmsg = 54 | "遇到错误, 远端服务器返回错误, 代码: 31066, 消息: 文件或目录不存在"; 55 | export { sleep }; 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # baidupcs-batch-upload 2 | 3 | https://github.com/iikira/BaiduPCS-Go 4 | 5 | 使用 `baidupcs-go`,百度网盘批量上传文件,高并发支持 6 | 7 | 在上传超过几百个文件时,速度快得多 8 | 9 | 给上传操作限流,防止打开过多报错,默认最多同时运行 15 个上传进程,可传参更多并发上传数 10 | 11 | 如果遇到 几 种网络问题上传失败,则自动重试,比直接用 一个 `BaiduPCS-Go` 命令上传更快 12 | 13 | 解决了上传文件有小概率上传未成功,但`baidupcs-go`返回上传成功的问题, 14 | 15 | 解决思路:使用`upload`上传完文件之后,使用`meta`命令,检查文件是否存在于网盘中,然后重传失败的文件 16 | 17 | # 目前适配的版本 18 | 19 | BaiduPCS-Go version v3.6.2 20 | 21 | BaiduPCS-Go-v3.6.2-windows-x64 22 | 23 | 下载网址 24 | 25 | https://github.com/masx200/baidupcs-batch-upload/releases/download/1.1.5/BaiduPCS-Go-v3.6.2-windows-x64.zip 26 | 27 | https://github.com/felixonmars/BaiduPCS-Go/releases/tag/v3.6.2 28 | 29 | https://github.com/masx200/baidupcs-batch-upload/releases/download/1.1.8/BaiduPCS-Go.arm64.android-v3.6.2-devel.rar 30 | 31 | # 使用方法 32 | 33 | 安装 34 | 35 | ```shell 36 | npm i -g @masx200/baidupcs-batch-upload 37 | ``` 38 | 39 | 或者 40 | 41 | ```shell 42 | yarn global add @masx200/baidupcs-batch-upload 43 | ``` 44 | 45 | ## 安装 `node_modules` 46 | 47 | ```shell 48 | yarn install 49 | ``` 50 | 51 | ## 编译脚本 52 | 53 | ```shell 54 | yarn build 55 | ``` 56 | 57 | ## 运行脚本 58 | 59 | ```shell 60 | yarn start 61 | ``` 62 | 63 | # 使用之前先登陆 64 | 65 | ```shell 66 | BaiduPCS-Go login 67 | ``` 68 | 69 | # 获取 bduss 的方法 70 | 71 | http://tool.cccyun.cc/tool/bduss/index3.html 72 | 73 | https://bduss.lingtings.com/index3.html 74 | 75 | # 命令行示例 76 | 77 | 必选参数 `input`:类型`string`,输入本地文件目录 78 | 79 | 必选参数 `dest`:类型`string`,上传到的网盘文件目录 80 | 81 | 可选参数`concurrent`:类型`number`,同时并发的上传文件个数 82 | 83 | ```shell 84 | 85 | npx @masx200/baidupcs-batch-upload --input=D:/baidupandownload/图片输入本地 --dest=/baidupandownload/图片输出网盘 86 | ``` 87 | 88 | ```shell 89 | npx @masx200/baidupcs-batch-upload --input=D:/baidupandownload/图片输入本地 --dest=/baidupandownload/图片输出网盘 --concurrent=20 90 | ``` 91 | 92 | 对于 windows 系统, 93 | 94 | 如果是带空格的本地目录/网盘目录场景, 95 | 96 | 可以使用如下命令: 97 | 98 | ```powershell 99 | 100 | baidupcs-batch-upload.cmd --input="d:\2 2" --dest="/test/t s" --concurrent=20 101 | ``` 102 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | yarn.lock 106 | -------------------------------------------------------------------------------- /lib/checkexist.ts: -------------------------------------------------------------------------------- 1 | import pupkg from "@shanyue/promise-utils"; 2 | import { checkmetamsg } from "./checkmetamsg.js"; 3 | import execmeta from "./execmeta.js"; 4 | import { onFailedAttempt } from "./onFailedAttempt.js"; 5 | 6 | export async function checkexist(remotefile: string): Promise { 7 | const fileexist = await retry( 8 | async () => { 9 | const result = await execmeta(remotefile); 10 | 11 | const { stdout, stderr } = result; 12 | 13 | //偶尔会发生奇怪的错误 14 | if (stdout === "" && stderr === "") { 15 | const e = Object.assign( 16 | new Error( 17 | "exec command failure! baidupcs-go:" + 18 | "\n" + 19 | stdout + 20 | "\n" + 21 | stderr 22 | ), 23 | { stdout, stderr } 24 | ); 25 | console.error(e); 26 | console.warn("运行命令查询错误,5秒后重试"); 27 | await sleep(5000); 28 | return await checkexist(remotefile); 29 | } 30 | /* const 记录日志 = { 31 | remotefile, 32 | stdout, 33 | stderr, 34 | }; 35 | console.log(JSON.stringify(记录日志, null, 4)); 36 | */ 37 | if (stdout.includes(notexistmsg)) { 38 | return false; 39 | } else if (checkmetamsg(stdout)) { 40 | return true; 41 | } else if (!checkmetamsg(stdout)) { 42 | return false; 43 | } else { 44 | throw Object.assign( 45 | new Error( 46 | "exec command failure! baidupcs-go:" + 47 | "\n" + 48 | stdout + 49 | "\n" + 50 | stderr 51 | ), 52 | { stdout, stderr } 53 | ); 54 | } 55 | }, 56 | { 57 | times: 15, 58 | onFailedAttempt: onFailedAttempt, 59 | } 60 | ); 61 | return fileexist; 62 | } 63 | 64 | const { retry, sleep } = pupkg; 65 | const notexistmsg = 66 | "遇到错误, 远端服务器返回错误, 代码: 31066, 消息: 文件或目录不存在"; 67 | export { sleep }; 68 | -------------------------------------------------------------------------------- /error-log/panic runtime error index out of range.txt: -------------------------------------------------------------------------------- 1 | Error: Command failed: BaiduPCS-Go.exe upload D:\baidupandownload\微博美图合集-2020-02-15+2020-02-14\图片压缩输出\微博美图合集-2020-02-14\教主Shadow\微博配图\882ab48745b65338f17c993f80d3f15c.webp /我的图片/微博美图合集-2020-02-15+2020-02-14/图片压缩输出/微博美图合集-2020-02-14/教主Shadow/微博配图 2 | 3 | panic: runtime error: index out of range 4 | 5 | goroutine 22 [running]: 6 | github.com/iikira/BaiduPCS-Go/internal/pcsfunctions/pcsupload.(*UploadingDatabase).UpdateUploading(0xc0422e48d0, 0xc042222240, 0xc0420f6ee0) 7 | /Users/syy/go/src/github.com/iikira/BaiduPCS-Go/internal/pcsfunctions/pcsupload/upload_database.go:101 +0x279 8 | github.com/iikira/BaiduPCS-Go/internal/pcscommand.RunUpload.func2.1(0xa60d20, 0xc042580a80, 0xc04205c240) 9 | /Users/syy/go/src/github.com/iikira/BaiduPCS-Go/internal/pcscommand/upload.go:355 +0x385 10 | github.com/iikira/BaiduPCS-Go/requester/uploader.(*MultiUploader).uploadStatusEvent.func1(0xc04206aa90) 11 | /Users/syy/go/src/github.com/iikira/BaiduPCS-Go/requester/uploader/status.go:93 +0x1a1 12 | created by github.com/iikira/BaiduPCS-Go/requester/uploader.(*MultiUploader).uploadStatusEvent 13 | /Users/syy/go/src/github.com/iikira/BaiduPCS-Go/requester/uploader/status.go:84 +0x4f 14 | 15 | at ChildProcess.exithandler (child_process.js:303:12) 16 | at ChildProcess.emit (events.js:321:20) 17 | at maybeClose (internal/child_process.js:1026:16) 18 | at Process.ChildProcess._handle.onexit (internal/child_process.js:286:5) 19 | 20 | { 21 | killed: false, 22 | code: 2, 23 | signal: null, 24 | cmd: 'BaiduPCS-Go.exe upload D:\\baidupandownload\\微博美图合集-2020-02-15+2020-02-14\\图片压缩输出\\微博美图合集-2020-02-14\\教主Shadow\\微博配图\\882ab48745b65338f17c993f80d3f15c.webp /我的图片/微博美图合集-2020-02-15+2020-02-14/图片压缩输出/微博美图合集-2020-02-14/教主Shadow/微博配图', 25 | stdout: '[1] 加入上传队列: D:/baidupandownload/微博美图合集-2020-02-15+2020-02-14/图片压缩输出/微博美图合集-2020-02-14/教主Shadow/微 博配图/882ab48745b65338f17c993f80d3f15c.webp\n' + 26 | '[1] 准备上传: D:/baidupandownload/微博美图合集-2020-02-15+2020-02-14/图片压缩输出/微博美图合集-2020-02-14/教主Shadow/微博配图/882ab48745b65338f17c993f80d3f15c.webp\n' + 27 | '[1] 秒传失败, 开始上传文件...\n' + 28 | '\n' + 29 | '\n' + 30 | '[1] 上传文件成功, 保存到网盘路径: /我的图片/微博美图合集-2020-02-15+2020-02-14/图片压缩输出/微博美图合集-2020-02-14/教主Shadow/微 博配图/882ab48745b65338f17c993f80d3f15c.webp\n', 31 | stderr: 'panic: runtime error: index out of range\n' + 32 | '\n' + 33 | 'goroutine 22 [running]:\n' + 34 | 'github.com/iikira/BaiduPCS-Go/internal/pcsfunctions/pcsupload.(*UploadingDatabase).UpdateUploading(0xc0422e48d0, 0xc042222240, 0xc0420f6ee0)\n' + 35 | '\t/Users/syy/go/src/github.com/iikira/BaiduPCS-Go/internal/pcsfunctions/pcsupload/upload_database.go:101 +0x279\n' + 36 | 'github.com/iikira/BaiduPCS-Go/internal/pcscommand.RunUpload.func2.1(0xa60d20, 0xc042580a80, 0xc04205c240)\n' + 37 | '\t/Users/syy/go/src/github.com/iikira/BaiduPCS-Go/internal/pcscommand/upload.go:355 +0x385\n' + 38 | 'github.com/iikira/BaiduPCS-Go/requester/uploader.(*MultiUploader).uploadStatusEvent.func1(0xc04206aa90)\n' + 39 | '\t/Users/syy/go/src/github.com/iikira/BaiduPCS-Go/requester/uploader/status.go:93 +0x1a1\n' + 40 | 'created by github.com/iikira/BaiduPCS-Go/requester/uploader.(*MultiUploader).uploadStatusEvent\n' + 41 | '\t/Users/syy/go/src/github.com/iikira/BaiduPCS-Go/requester/uploader/status.go:84 +0x4f\n' 42 | } 43 | -------------------------------------------------------------------------------- /success-log/panic runtime error index out of range.txt: -------------------------------------------------------------------------------- 1 | file:///C:/Users/ma/AppData/Local/Yarn/Data/global/node_modules/@masx200/baidupcs-batch-upload/lib/index.js:9 2 | throw e; 3 | ^ 4 | 5 | Error: Command failed: BaiduPCS-Go.exe upload d:\Pictures\手机相册\手机相册-2020年7月9日\Screenshots\24b8595ffb846b16d8106adc222b2d08.webp /我的图片-2020年6月25日/手机相册/手机相册-2020年7月9日/Screenshots 6 | panic: runtime error: index out of range 7 | 8 | goroutine 25 [running]: 9 | github.com/iikira/BaiduPCS-Go/internal/pcsfunctions/pcsupload.(*UploadingDatabase).UpdateUploading(0xc0422de8d0, 0xc0421e23f0, 0xc0422ce9c0) 10 | /Users/syy/go/src/github.com/iikira/BaiduPCS-Go/internal/pcsfunctions/pcsupload/upload_database.go:101 +0x279 11 | github.com/iikira/BaiduPCS-Go/internal/pcsfunctions/pcsupload.(*UploadTaskUnit).upload.func1(0xa65160, 0xc04237a700, 0xc0422bc0c0) 12 | /Users/syy/go/src/github.com/iikira/BaiduPCS-Go/internal/pcsfunctions/pcsupload/upload_task_unit.go:208 +0x399 13 | github.com/iikira/BaiduPCS-Go/requester/uploader.(*MultiUploader).uploadStatusEvent.func1(0xc042278410) 14 | /Users/syy/go/src/github.com/iikira/BaiduPCS-Go/requester/uploader/status.go:93 +0x1a1 15 | created by github.com/iikira/BaiduPCS-Go/requester/uploader.(*MultiUploader).uploadStatusEvent 16 | /Users/syy/go/src/github.com/iikira/BaiduPCS-Go/requester/uploader/status.go:84 +0x4f 17 | 18 | at ChildProcess.exithandler (child_process.js:303:12) 19 | at ChildProcess.emit (events.js:315:20) 20 | at maybeClose (internal/child_process.js:1051:16) 21 | at Process.ChildProcess._handle.onexit (internal/child_process.js:287:5) { 22 | killed: false, 23 | code: 2, 24 | signal: null, 25 | cmd: 'BaiduPCS-Go.exe upload d:\\Pictures\\手机相册\\手机相册-2020年7月9日\\Screenshots\\24b8595ffb846b16d8106adc222b2d08.webp /我的图片-2020年6月25日/手机相册/手机相册-2020年7月9日/Screenshots', 26 | stdout: '[1] 加入上传队列: d:/Pictures/手机相册/手机相册-2020年7月9日/Screenshots/24b8595ffb846b16d8106adc222b2d08.webp\n' + 27 | '[1] 准备上传: d:/Pictures/手机相册/手机相册-2020年7月9日/Screenshots/24b8595ffb846b16d8106adc222b2d08.webp\n' + 28 | '[1] 秒传失败, 开始上传文件...\n' + 29 | '\n' + 30 | '\n' + 31 | '[1] 上传文件成功, 保存到网盘路径: /我的图片-2020年6月25日/手机相册/手机相册-2020年7月9日/Screenshots/24b8595ffb846b16d8106adc222b2d08.webp\n', 32 | stderr: 'panic: runtime error: index out of range\n' + 33 | '\n' + 34 | 'goroutine 25 [running]:\n' + 35 | 'github.com/iikira/BaiduPCS-Go/internal/pcsfunctions/pcsupload.(*UploadingDatabase).UpdateUploading(0xc0422de8d0, 0xc0421e23f0, 0xc0422ce9c0)\n' + 36 | '\t/Users/syy/go/src/github.com/iikira/BaiduPCS-Go/internal/pcsfunctions/pcsupload/upload_database.go:101 +0x279\n' + 37 | 'github.com/iikira/BaiduPCS-Go/internal/pcsfunctions/pcsupload.(*UploadTaskUnit).upload.func1(0xa65160, 0xc04237a700, 0xc0422bc0c0)\n' + 38 | '\t/Users/syy/go/src/github.com/iikira/BaiduPCS-Go/internal/pcsfunctions/pcsupload/upload_task_unit.go:208 +0x399\n' + 39 | 'github.com/iikira/BaiduPCS-Go/requester/uploader.(*MultiUploader).uploadStatusEvent.func1(0xc042278410)\n' + 40 | '\t/Users/syy/go/src/github.com/iikira/BaiduPCS-Go/requester/uploader/status.go:93 +0x1a1\n' + 41 | 'created by github.com/iikira/BaiduPCS-Go/requester/uploader.(*MultiUploader).uploadStatusEvent\n' + '\t/Users/syy/go/src/github.com/iikira/BaiduPCS-Go/requester/uploader/status.go:84 +0x4f\n' 42 | } 43 | -------------------------------------------------------------------------------- /lib/upload.js: -------------------------------------------------------------------------------- 1 | import baidupcsupload from "./execbaidupcs.js"; 2 | import { 3 | directfailure, 4 | successerror, 5 | successmsg, 6 | fatalerror, 7 | retrymsg, 8 | } from "./uploadfile.js"; 9 | import { retryupload } from "./retryupload.js"; 10 | export async function upload(file, destination) { 11 | const starttime = Date.now(); 12 | let result = { stdout: "", stderr: "" }; 13 | function done() { 14 | const endtime = Date.now(); 15 | const durtime = (endtime - starttime) / 1000; 16 | console.log(["初步文件上传成功", file, destination].join("\n")); 17 | console.log("用时" + durtime + "秒"); 18 | return; 19 | } 20 | try { 21 | result = await baidupcsupload(file, destination); 22 | } catch (error) { 23 | const { stdout, stderr, code } = error; 24 | console.error(error); 25 | console.error(JSON.stringify({ stdout, stderr }, null, 4)); 26 | if ( 27 | typeof code !== "number" || 28 | typeof stdout !== "string" || 29 | typeof stderr !== "string" 30 | ) { 31 | throw error; 32 | } else if ( 33 | code === 2 && 34 | !directfailure.some((m) => stdout.includes(m)) && 35 | successerror.some((m) => stderr?.includes(m)) && 36 | successmsg.some((m) => stdout?.includes(m)) 37 | ) { 38 | done(); 39 | return; 40 | } else { 41 | throw error; 42 | } 43 | } 44 | const { stdout, stderr } = result; 45 | if (fatalerror.some((m) => stdout.includes(m))) { 46 | throw Object.assign( 47 | new Error( 48 | "exec command failure! baidupcs-go:" + 49 | "\n" + 50 | stdout + 51 | "\n" + 52 | stderr 53 | ), 54 | { stdout, stderr } 55 | ); 56 | } else if (directfailure.some((m) => stdout.includes(m))) { 57 | const e = Object.assign( 58 | new Error( 59 | "exec command failure! baidupcs-go:" + 60 | "\n" + 61 | stdout + 62 | "\n" + 63 | stderr 64 | ), 65 | { stdout, stderr } 66 | ); 67 | console.error(e); 68 | console.warn(stdout, stderr); 69 | console.warn("上传失败,5秒后重试:" + file); 70 | return await retryupload(file, destination); 71 | } else if (successmsg.some((m) => stdout.includes(m))) { 72 | done(); 73 | return; 74 | } else if (retrymsg.some((msg) => stdout.includes(msg))) { 75 | const e = Object.assign( 76 | new Error( 77 | "exec command failure! baidupcs-go:" + 78 | "\n" + 79 | stdout + 80 | "\n" + 81 | stderr 82 | ), 83 | { stdout, stderr } 84 | ); 85 | console.error(e); 86 | console.warn(stdout, stderr); 87 | console.warn("上传失败,5秒后重试:" + file); 88 | return await retryupload(file, destination); 89 | } else { 90 | throw Object.assign( 91 | new Error( 92 | "exec command failure! baidupcs-go:" + 93 | "\n" + 94 | stdout + 95 | "\n" + 96 | stderr 97 | ), 98 | { stdout, stderr } 99 | ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /lib/upload.ts: -------------------------------------------------------------------------------- 1 | import baidupcsupload from "./execbaidupcs.js"; 2 | import { 3 | directfailure, 4 | successerror, 5 | successmsg, 6 | fatalerror, 7 | retrymsg, 8 | } from "./uploadfile.js"; 9 | import { retryupload } from "./retryupload.js"; 10 | 11 | /** 12 | * @param {string} file 13 | * @param {string} destination 14 | */ 15 | 16 | export async function upload(file: string, destination: string): Promise { 17 | // const localfile = file; 18 | // const desdir = destination; 19 | const starttime = Date.now(); 20 | 21 | let result = { stdout: "", stderr: "" }; 22 | function done() { 23 | const endtime = Date.now(); 24 | const durtime = (endtime - starttime) / 1000; 25 | 26 | console.log(["初步文件上传成功", file, destination].join("\n")); 27 | 28 | console.log("用时" + durtime + "秒"); 29 | 30 | return; 31 | } 32 | try { 33 | result = await baidupcsupload(file, destination); 34 | } catch (error) { 35 | const { stdout, stderr, code } = error; 36 | console.error(error); 37 | console.error(JSON.stringify({ stdout, stderr }, null, 4)); 38 | 39 | if ( 40 | typeof code !== "number" || 41 | typeof stdout !== "string" || 42 | typeof stderr !== "string" 43 | ) { 44 | throw error; 45 | } else if ( 46 | code === 2 && 47 | !directfailure.some((m) => stdout.includes(m)) && 48 | successerror.some((m) => stderr?.includes(m)) && 49 | successmsg.some((m) => stdout?.includes(m)) 50 | ) { 51 | /* 52 | const endtime=Date.now() 53 | const durtime=(endtime-starttime)/1000 54 | 55 | 56 | console.log(["初步文件上传成功", file, destination,].join("\n")); 57 | 58 | console.log("用时"+durtime+"秒") 59 | */ 60 | done(); 61 | return; 62 | } else { 63 | //如果。找不到 baidupcs-go的可执行文件,则。会在这里报错 64 | throw error; 65 | } 66 | } 67 | 68 | const { stdout, stderr } = result; 69 | /* const 记录日志 = { 70 | localfile, 71 | desdir, 72 | stdout, 73 | stderr, 74 | }; 75 | console.log(JSON.stringify(记录日志, null, 4)); 76 | */ 77 | //未登录的致命错误要失败 78 | /* 判断是否上传成功与失败 */ 79 | if (fatalerror.some((m) => stdout.includes(m))) { 80 | throw Object.assign( 81 | new Error( 82 | "exec command failure! baidupcs-go:" + 83 | "\n" + 84 | stdout + 85 | "\n" + 86 | stderr 87 | ), 88 | { stdout, stderr } 89 | ); 90 | /*throw new Error( 91 | "exec command failure! baidupcs-go:" + "\n" + stdout + "\n" + stderr 92 | );*/ 93 | } else if (directfailure.some((m) => stdout.includes(m))) { 94 | const e = Object.assign( 95 | new Error( 96 | "exec command failure! baidupcs-go:" + 97 | "\n" + 98 | stdout + 99 | "\n" + 100 | stderr 101 | ), 102 | { stdout, stderr } 103 | ); 104 | console.error(e); 105 | console.warn(stdout, stderr); 106 | console.warn("上传失败,5秒后重试:" + file); 107 | return await retryupload(file, destination); 108 | } else if (successmsg.some((m) => stdout.includes(m))) { 109 | // console.log("初步文件上传成功", file, destination); 110 | done(); 111 | return; 112 | } else if (retrymsg.some((msg) => stdout.includes(msg))) { 113 | const e = Object.assign( 114 | new Error( 115 | "exec command failure! baidupcs-go:" + 116 | "\n" + 117 | stdout + 118 | "\n" + 119 | stderr 120 | ), 121 | { stdout, stderr } 122 | ); 123 | console.error(e); 124 | console.warn(stdout, stderr); 125 | console.warn("上传失败,5秒后重试:" + file); 126 | 127 | return await retryupload(file, destination); 128 | } else { 129 | /*throw new Error( 130 | "exec command failure! baidupcs-go:" + "\n" + stdout + "\n" + stderr 131 | );*/ 132 | throw Object.assign( 133 | new Error( 134 | "exec command failure! baidupcs-go:" + 135 | "\n" + 136 | stdout + 137 | "\n" + 138 | stderr 139 | ), 140 | { stdout, stderr } 141 | ); 142 | } 143 | } 144 | 145 | /* 146 | Error: spawn BaiduPCS-Go ENOENT 147 | at Process.ChildProcess._handle.onexit (internal/child_process.js:268:19) 148 | at onErrorNT (internal/child_process.js:468:16) 149 | at processTicksAndRejections (internal/process/task_queues.js:84:21) { 150 | errno: -2, 151 | code: 'ENOENT', 152 | syscall: 'spawn BaiduPCS-Go', 153 | path: 'BaiduPCS-Go', 154 | spawnargs: [ 'upload', '/sdcard/test/test/文件.txt', '/我 的测试/test' ], 155 | cmd: 'BaiduPCS-Go upload /sdcard/test/test/文件.txt /我的 测试/test', 156 | stdout: '', 157 | stderr: '' 158 | } 159 | 160 | */ 161 | /* 162 | Error: Command failed: BaiduPCS-Go.exe upload D:\baidupandownload\微博美图合集-2020-02-15+2020-02-14\图片压缩输出\微博美图合集-2020-02-14\教主Shadow\微博配图\882ab48745b65338f17c993f80d3f15c.webp /我的图片/微博美图合集-2020-02-15+2020-02-14/图片压缩输出/微博美图合集-2020-02-14/教主Shadow/微博配图 panic: runtime error: index out of range goroutine 22 [running]:github.com/iikira/BaiduPCS-Go/internal/pcsfunctions/pcsupload.(*UploadingDatabase).UpdateUploading(0xc0422e48d0, 0xc042222240, 0xc0420f6ee0) /Users/syy/go/src/github.com/iikira/BaiduPCS-Go/internal/pcsfunctions/pcsupload/upload_database.go:101 +0x279github.com/iikira/BaiduPCS-Go/internal/pcscommand.RunUpload.func2.1(0xa60d20, 0xc042580a80, 0xc04205c240) /Users/syy/go/src/github.com/iikira/BaiduPCS-Go/internal/pcscommand/upload.go:355 +0x385github.com/iikira/BaiduPCS-Go/requester/uploader.(*MultiUploader).uploadStatusEvent.func1(0xc04206aa90) /Users/syy/go/src/github.com/iikira/BaiduPCS-Go/requester/uploader/status.go:93 +0x1a1created by github.com/iikira/BaiduPCS-Go/requester/uploader.(*MultiUploader).uploadStatusEvent /Users/syy/go/src/github.com/iikira/BaiduPCS-Go/requester/uploader/status.go:84 +0x4f at ChildProcess.exithandler (child_process.js:303:12) at ChildProcess.emit (events.js:321:20) at maybeClose (internal/child_process.js:1026:16) at Process.ChildProcess._handle.onexit (internal/child_process.js:286:5) { killed: false, code: 2, signal: null, cmd: 'BaiduPCS-Go.exe upload D:\\baidupandownload\\微博美图合集-2020-02-15+2020-02-14\\图片压缩输出\\微博美图合集-2020-02-14\\教主Shadow\\微博配图\\882ab48745b65338f17c993f80d3f15c.webp /我的图片/微博美图合集-2020-02-15+2020-02-14/图片压缩输出/微博美图合集-2020-02-14/教主Shadow/微博配图', 163 | 164 | */ 165 | --------------------------------------------------------------------------------