Skip to content
nodejs实现夸克网盘转存

新建文件夹并初始化

js
npm init -y

安装依赖包

js
npm install axios

创建config.js文件

js
module.exports = {
  // 必填:把你浏览器里完整的 Cookie 字符串粘过来
  cookie: '',
  // 可选:接口限流,单位 ms(默认20000,也就是批量上传,每两个间隔20秒上传一个文件,防止限流,不做批量上传的话可以默认0)
  sleep: 20000,
  // 目前存储文件夹id(网盘里面详情信息里面有,自行去找,会将文件存储在这个id的文件夹下)
  folderId: '',
};

links.txt(存放链接的文件)

js
https://pan.quark.cn/s/70d9657c8678
https://pan.quark.cn/s/fba1d65775c4
https://pan.quark.cn/s/c9dfc984b065
https://pan.quark.cn/s/3b74b82acb35
https://pan.quark.cn/s/fa6f880dc34c
https://pan.quark.cn/s/1d2efc94d5d1
https://pan.quark.cn/s/0b767466979f
https://pan.quark.cn/s/a36a201e1791
https://pan.quark.cn/s/d794590ab6a2
https://pan.quark.cn/s/bc8e8c875074
https://pan.quark.cn/s/e25266fc45fc
https://pan.quark.cn/s/3a5280f8fb0a
https://pan.quark.cn/s/e17d05118faf
https://pan.quark.cn/s/fb26e1744219
https://pan.quark.cn/s/231ed85ba147
https://pan.quark.cn/s/d79e071b9e5e
https://pan.quark.cn/s/d46f18e025bd
https://pan.quark.cn/s/2fca2e371822
https://pan.quark.cn/s/59267f475240
https://pan.quark.cn/s/63f0816ab04c
https://pan.quark.cn/s/a069f3539fbd
https://pan.quark.cn/s/00955b40360a
https://pan.quark.cn/s/2710277e928d
https://pan.quark.cn/s/b560a2e26715
https://pan.quark.cn/s/186908545292
https://pan.quark.cn/s/06ab67f3a76e
https://pan.quark.cn/s/fb3c3fd7fe52

index.js

js
/**
 * 夸克网盘分享链接批量转存 —— 2025-06-18 官方路径实测
 * 依赖:npm i axios
 */
const fs = require('fs');
const path = require('path');
const axios = require('axios');
const { cookie, sleep, folderId } = require('./config');
let flag = true;
let timer = null;
// 节流函数
const sleep2 = (ms) => {
    // 返回promise
    return new Promise((resolve, reject) => {
        // 节流
        if (flag) {
            flag = false;
            timer = setTimeout(() => {
                flag = true;
                clearTimeout(timer);
                timer = null;
                resolve();
            }, ms);
        }
    });
}


const client = axios.create({
    baseURL: 'https://drive-h.quark.cn',
    headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        Cookie: cookie,
        'Accept': 'application/json'
    },
});

/* ===== 请求拦截器:打印真正发出去的完整信息 ===== */
client.interceptors.request.use(config => {
    return config;
});

/* ===== 响应拦截器:打印返回状态 & 简要数据 ===== */
client.interceptors.response.use(
    res => {
        // console.log('[<<< RESPONSE <<<]');
        // console.log('Status :', res.status);
        // console.log('Data   :', res.data);
        return res;
    },
    err => {
        console.log('[<<< ERROR <<<]');
        if (err.response) {
            console.log('Status :', err.response.status, err.response.statusText);
            console.log('URL    :', err.config?.baseURL + err.config?.url);
            console.log('Params :', JSON.stringify(err.config?.params || {}, null, 2));
            console.log('Body   :', JSON.stringify(err.config?.data || {}, null, 2));
            console.log('Resp   :', JSON.stringify(err.response.data, null, 2));
        } else {
            console.log('Message:', err.message);
        }
        return Promise.reject(err);
    }
);

(async () => {
    const links = fs.readFileSync(path.join(__dirname, 'links.txt'), 'utf8')
        .split(/\r?\n/)
        .map(l => l.trim())
        .filter(Boolean);

    for (const shareUrl of links) {
        try {
            const { shareId, pwd } = parseShareUrl(shareUrl);
            console.log(`\n开始转存:${shareId}`);

            console.log('① 获取 token...');
            const { stoken } = await getToken(shareId, pwd);

            console.log('② 获取文件列表...');
            const files = await getFileList(shareId, stoken);
            if (!files.length) { console.log('分享为空'); continue; }

            console.log('④ 转存中...');
            let res = await saveFiles(shareId, stoken, folderId);
            if (res) {
                console.log(`✅ 转存完成:${shareId}`);
            } else {
                console.log(`❌ 转存失败:${shareId}`);
            }
            // 5. 生成新分享(仅成功)
            console.log('⑤ 生成分享链接...');
            const newShare = await createShare(res); // 目录id
            console.log('✅ 分享链接:', newShare.url);
            await sleep2(sleep);
        } catch (e) {
            await sleep2(sleep);
            console.error(`❌ 失败:${shareUrl}`, e.message);
        }
    }
})();
/**
 * 生成分享链接
 * @param {string[]} fids  要分享的文件id
 */
async function createShare(fids) {
    // 创建分享
    const res = await client.post(
        '/1/clouddrive/share?pr=ucpro&fr=pc',
        {
            fid_list: fids,
            expired_type: 1,   // 永久
            title: '批量分享',
            url_type: 1,       // 2=私密(会返回 passcode)
        }
    );
    console.log('创建分享成功:', res.data);
    // 2. 轮询任务直到完成
    for (let i = 0; i < 60; i++) {          // 最多 60 秒
        const { data } = await client.get(
            '/1/clouddrive/task?pr=ucpro&fr=pc',
            { params: { task_id: res.data.data.task_id } }
        );
        const d = data.data;
        if (data.status === 200 && d?.share_id) {
            // 调用分享
            const res3 = await client.post(
                '/1/clouddrive/share/password?pr=ucpro&fr=pc',
                {
                    share_id: d.share_id
                }
            );
            if (res3.data.status == 200) {
                console.log(`✅ 分享成功:${res3.data.data.share_url}`);
                return {
                    url: res3.data.data.share_url
                }
            } else {
                console.error(`❌ 创建分享失败:${res3.data.message}`);
                return null;
            }
        }
        await new Promise(r => setTimeout(r, 1000));
    }
    throw new Error('轮询超时,未拿到新文件 id');
}

/* ---------- 工具函数 ---------- */
function parseShareUrl(url) {
    const u = new URL(url);
    const shareId = u.pathname.split('/s/')[1].split(/[/#?]/)[0];
    const pwd = u.searchParams.get('pwd') || '';
    return { shareId, pwd };
}

async function getToken(shareId, pwd) {
    const url = '/1/clouddrive/share/sharepage/token?pr=ucpro&fr=pc&uc_param_str=&__dt=176&__t=1757468424118';
    console.log('[TOKEN REQ]', url, { share_id: shareId, passcode: pwd });

    const { data } = await client.post(url, {
        pwd_id: shareId,
        support_visit_limit_private_share: true,
        passcode: pwd || ''          // 确保没密码时传空串
    });

    return { stoken: data.data.stoken };
}

async function getFileList(shareId, stoken) {
    const { data } = await client.get(
        '/1/clouddrive/share/sharepage/detail?pr=ucpro&fr=pc',
        { params: { pwd_id: shareId, stoken, _size: 50 } }
    );
    return data.data.list || [];
}


async function saveFiles(shareId, stoken, toPid) {
    // 1. 发起保存(保存全部)
    const { data: save } = await client.post(
        '/1/clouddrive/share/sharepage/save?pr=ucpro&fr=pc',
        {
            exclude_fids: [],
            fid_token_list: [],
            pdir_fid: '0',
            pwd_id: shareId,
            stoken,
            fid_list: [],              // 空 = 保存全部
            to_pdir_fid: toPid,
            scene: 'link',
            pdir_save_all: true
        }
    );

    const taskId = save.data.task_id;
    console.log('✅ 保存任务已提交,task_id:', taskId);

    // 2. 轮询直到完成
    for (let i = 0; i < 120; i++) { // 最多 120 秒
        const { data } = await client.get(
            '/1/clouddrive/task?pr=ucpro&fr=pc',
            { params: { task_id: taskId } }
        );

        const d = data?.data;
        const status = data?.status;

        console.log(`[轮询 ${i + 1}] status=${status}, save_as_top_fids=`, d?.save_as?.save_as_top_fids);

        if (status === 200 && d?.save_as?.save_as_top_fids?.length > 0) {
            console.log('✅ 转存完成,文件ID列表:', d.save_as.save_as_top_fids);
            return d.save_as.save_as_top_fids;
        }

        await new Promise(r => setTimeout(r, 1000));
    }

    throw new Error('❌ 转存任务超时(120秒),仍未完成');
}

运行

js
node index.js

代码运行后就开始将上述网盘文件存储到指定的文件夹id下面,并分享出这个文件夹新的分享链接