说明
做文件更新的时候,一个好用而又简单的下载路径是比较重要的。
本来考虑的是在软件中做解析,但如果官方接口更改的话,那么无法更新的话,软件主体就需要客户手动进行更新了。这样更为麻烦。
也可以根据下面的PHP代码,直接做一个请求接口,返回第一个附件的下载地址用来做自动更新。这样也更为省事。
测试图片
测试地址
https://share.note.youdao.com/s/SAGUBzGP
使用说明
将PHP代码上传至服务器中,加入参数:share=SAGUBzGP
如:https://xxx.com/youdao.php?share=SAGUBzGP
PHP代码
<?php
if (isset($_GET['api']) && isset($_GET['share'])) {
header('Content-Type: application/json; charset=utf-8');
function jsonResponse($code, $data) {
echo json_encode(['code' => $code, 'data' => $data], JSON_UNESCAPED_UNICODE);
exit;
}
$shareId = $_GET['share'];
if (!preg_match('/^[a-zA-Z0-9]+$/', $shareId)) {
jsonResponse(1, '无效 share 参数');
}
$headers = @get_headers('https://note.youdao.com/s/' . $shareId, 1);
if (!$headers || !isset($headers['Location'])) {
jsonResponse(1, '未能解析 note ID');
}
$loc = is_array($headers['Location']) ? end($headers['Location']) : $headers['Location'];
parse_str(parse_url($loc, PHP_URL_QUERY), $params);
if (empty($params['id'])) {
jsonResponse(1, '未解析到 ID');
}
$noteId = $params['id'];
$apiUrl = "https://note.youdao.com/yws/api/note/{$noteId}?sev=j1&editorType=1&editorVersion=new-json-editor";
$jsonStr = @file_get_contents($apiUrl);
if (!$jsonStr) {
jsonResponse(1, '获取内容失败');
}
$resp = json_decode($jsonStr, true);
if (json_last_error() !== JSON_ERROR_NONE || empty($resp['content'])) {
jsonResponse(1, '解析失败');
}
$data = json_decode($resp['content'], true);
if (json_last_error() !== JSON_ERROR_NONE || empty($data['5'])) {
jsonResponse(1, '缺少数据');
}
$results = [];
foreach ($data['5'] as $item) {
if (!isset($item['4']['fn'], $item['4']['re'])) continue;
$name = $item['4']['fn'];
$src = $item['4']['re'];
$finalUrl = '';
$ch = curl_init($src);
curl_setopt_array($ch, [
CURLOPT_NOBODY => true,
CURLOPT_HEADER => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_USERAGENT => 'Mozilla/5.0'
]);
$headerStr = curl_exec($ch);
curl_close($ch);
if (preg_match('/Location:\s*(\S+)/i', $headerStr, $match)) {
$finalUrl = trim($match[1]);
} else {
$finalUrl = $src;
}
$results[] = ['name' => $name, 'url' => $finalUrl];
}
if (!empty($results)) {
jsonResponse(0, $results);
}
jsonResponse(1, '未找到附件');
}
?>
<!DOCTYPE html>
<html lang="zh-CN" class="scroll-smooth">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>有道云笔记下载</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
@layer utilities {
.bg-gradient { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
}
[v-cloak] { display: none; }
</style>
</head>
<body class="min-h-screen bg-gray-50 flex flex-col">
<header class="bg-gradient text-white py-12 text-center">
<h1 class="text-4xl font-extrabold">有道云笔记下载</h1>
<p class="mt-2 text-lg opacity-90">获取云笔记中的所有文件链接</p>
</header>
<main id="app" v-cloak class="flex-grow container mx-auto px-4 py-8">
<div v-if="!loaded && !error" class="flex justify-center items-center">
<svg class="animate-spin h-8 w-8 text-purple-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8H4z"></path>
</svg>
<p class="ml-3 text-gray-700">加载中,请稍候...</p>
</div>
<div v-if="error" class="text-center text-red-600 font-semibold">❌ {{ error }}</div>
<div v-if="files.length" class="space-y-4">
<div v-for="file in files" :key="file.url" class="p-4 bg-white rounded-lg shadow hover:shadow-lg transition">
<div class="flex items-center justify-between gap-4">
<div class="max-w-full overflow-hidden">
<p class="font-medium text-gray-800">{{ file.name }}</p>
<a :href="file.url"
class="text-sm text-indigo-600 hover:underline inline-block truncate max-w-full"
target="_blank"
:title="file.url">{{ file.url }}</a>
</div>
<button @click="open(file.url)"
class="px-4 py-2 bg-indigo-600 text-white rounded hover:bg-indigo-700 whitespace-nowrap">
下载
</button>
</div>
</div>
</div>
</main>
<footer class="text-center py-4 text-sm text-gray-500">精易论坛 LaoDan © 2025</footer>
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script>
const { createApp } = Vue;
createApp({
data() { return { files: [], error: '', loaded: false }; },
methods: {
open(url) { window.open(url, '_blank'); },
fetchData(id) {
fetch(`?api=1&share=${encodeURIComponent(id)}`)
.then(r => r.json())
.then(d => d.code === 0 ? this.files = d.data : this.error = d.data)
.catch(() => this.error = '网络错误')
.finally(() => this.loaded = true);
}
},
mounted() {
const id = new URLSearchParams(location.search).get('share');
if (!id) { this.error = '缺少 share 参数'; this.loaded = true; return; }
this.fetchData(id);
}
}).mount('#app');
</script>
</body>
</html>