html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- <script src="./indexedDBStore.js"></script> -->
<script>
class IndexedDBStore {
/**
* @param {string} dbName - 数据库名称
* @param {number} version - 数据库版本
* @param {Object} storeConfig - 存储配置 { name: 存储名称, keyPath: 主键, indexes: 索引数组 }
*/
constructor(dbName, version, storeConfig) {
this.dbName = dbName;
this.version = version;
this.storeConfig = storeConfig;
this.db = null;
}
/**
* 打开数据库连接
* @returns {Promise<IDBDatabase>}
*/
open() {
return new Promise((resolve, reject) => {
if (this.db) return resolve(this.db);
const request = indexedDB.open(this.dbName, this.version);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains(this.storeConfig.name)) {
const store = db.createObjectStore(this.storeConfig.name, {
keyPath: this.storeConfig.keyPath,
autoIncrement: this.storeConfig.autoIncrement || false
});
this.storeConfig.indexes?.forEach(index => {
store.createIndex(index.name, index.keyPath, index.options);
});
}
};
request.onsuccess = (event) => {
this.db = event.target.result;
resolve(this.db);
};
request.onerror = (event) => {
reject(`IndexedDB error: ${event.target.error}`);
};
});
}
/**
* 添加数据
* @param {Object} data - 要存储的数据
* @returns {Promise<IDBValidKey>}
*/
async add(data) {
const db = await this.open();
return new Promise((resolve, reject) => {
const transaction = db.transaction(this.storeConfig.name, 'readwrite');
const store = transaction.objectStore(this.storeConfig.name);
const request = store.add(data);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
/**
* 根据主键获取数据
* @param {IDBValidKey} key - 主键值
* @returns {Promise<Object>}
*/
async get(key) {
const db = await this.open();
return new Promise((resolve, reject) => {
const transaction = db.transaction(this.storeConfig.name, 'readonly');
const store = transaction.objectStore(this.storeConfig.name);
const request = store.get(key);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
/**
* 更新数据
* @param {Object} data - 要更新的数据(必须包含主键)
* @returns {Promise<IDBValidKey>}
*/
async update(data) {
const db = await this.open();
return new Promise((resolve, reject) => {
const transaction = db.transaction(this.storeConfig.name, 'readwrite');
const store = transaction.objectStore(this.storeConfig.name);
const request = store.put(data);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
/**
* 删除数据
* @param {IDBValidKey} key - 要删除的主键值
* @returns {Promise<void>}
*/
async delete(key) {
const db = await this.open();
return new Promise((resolve, reject) => {
const transaction = db.transaction(this.storeConfig.name, 'readwrite');
const store = transaction.objectStore(this.storeConfig.name);
const request = store.delete(key);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}
/**
* 查询数据(通过索引)
* @param {string} indexName - 索引名称
* @param {IDBValidKey} value - 查询值
* @returns {Promise<Array>}
*/
async queryByIndex(indexName, value) {
const db = await this.open();
return new Promise((resolve, reject) => {
const transaction = db.transaction(this.storeConfig.name, 'readonly');
const store = transaction.objectStore(this.storeConfig.name);
const index = store.index(indexName);
const request = index.getAll(value);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
/**
* 获取所有数据
* @returns {Promise<Array>}
*/
async getAll() {
const db = await this.open();
return new Promise((resolve, reject) => {
const transaction = db.transaction(this.storeConfig.name, 'readonly');
const store = transaction.objectStore(this.storeConfig.name);
const request = store.getAll();
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
/**
* 清空存储
* @returns {Promise<void>}
*/
async clear() {
const db = await this.open();
return new Promise((resolve, reject) => {
const transaction = db.transaction(this.storeConfig.name, 'readwrite');
const store = transaction.objectStore(this.storeConfig.name);
const request = store.clear();
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}
}
// 使用示例
const dbConfig = {
name: 'data',
keyPath: 'id',
autoIncrement: true,
indexes: [
{ name: 'url', keyPath: 'url', options: { unique: true } },
]
// indexes: [
// { name: 'email', keyPath: 'email', options: { unique: true } },
// { name: 'age', keyPath: 'age' }
// ]
};
const dataDB = new IndexedDBStore('MyAppDB', 1, dbConfig);
// 初始化数据库
dataDB.open()
.then(() => console.log('Database initialized'))
.catch(err => console.error('Initialization failed:', err));
// 添加用户
async function addData(data) {
try {
await dataDB.add(data);
console.log('Data added');
} catch (error) {
console.error('Add failed:', error);
}
}
// 查询所有用户
async function getAllData() {
try {
const data = await dataDB.getAll();
console.log('All data:', data);
return data;
} catch (error) {
console.error('Query failed:', error);
}
}
async function getData(url) {
try {
const data = await dataDB.queryByIndex('url', url)
console.log('All data:', data);
return data;
} catch (error) {
console.error('Query failed:', error);
}
}
let urlList = [
"https://vd3.bdstatic.com/mda-ni8zjpqumj0zq8wu/sc/cae_h264/1662767674990457142/mda-ni8zjpqumj0zq8wu.mp4?v_from_s=hkapp-haokan-hbf&auth_key=1739475858-0-0-5ab2399f0e9fb1ebe93cc98ee943b9d1&bcevod_channel=searchbox_feed&cr=2&cd=0&pd=1&pt=3&logid=2658451388&vid=11439834437747274135&klogid=2658451388&abtest=132219_1",
// "https://vd3.bdstatic.com/mda-qdd9c4qibyb9ewxr/sc/cae_h264/1713181094724751050/mda-qdd9c4qibyb9ewxr.mp4?v_from_s=hkapp-haokan-hbf&auth_key=1739475666-0-0-4a72becac12543d6f2039ea61a9fbdb4&bcevod_channel=searchbox_feed&cr=2&cd=0&pd=1&pt=3&logid=2465962025&vid=2939798464947657648&klogid=2465962025&abtest=132219_1",
// "https://vd3.bdstatic.com/mda-pbpjq8k4b30i7dta/sc/cae_h264/1677247731713061770/mda-pbpjq8k4b30i7dta.mp4?v_from_s=hkapp-haokan-hbf&auth_key=1739475438-0-0-f9784748f27a84e7eaf4a78d379cfad4&bcevod_channel=searchbox_feed&cr=2&cd=0&pd=1&pt=3&logid=2238334780&vid=9432685572508656171&klogid=2238334780&abtest=132219_1",
// "https://vd3.bdstatic.com/mda-rasicbaqwftn3x7w/sc/cae_h264/1737998688828146561/mda-rasicbaqwftn3x7w.mp4?v_from_s=hkapp-haokan-hbf&auth_key=1739474489-0-0-319d6636018c016959767f44a20648f0&bcevod_channel=searchbox_feed&cr=2&cd=0&pd=1&pt=3&logid=1289777015&vid=3911988374435305462&klogid=1289777015&abtest=132219_1",
]
urlList.forEach(item => {
addVideo("/?url="+item)
})
function addVideo(url) {
getData(url).then(res => {
console.log(res, 'res');
let videoItem = document.createElement("video");
videoItem.width = 400
videoItem.height = 300
videoItem.controls = true
videoItem.muted = true
videoItem.autoplay = true
document.body.appendChild(videoItem)
// setTimeout(() => {
// videoItem.muted = false;
// videoItem.play().then(() => {
// console.log('视频自动播放成功');
// }).catch((error) => {
// console.error('自动播放失败:', error);
// });// 取消静音
// }, 2000)
if (res.length > 0) {
videoItem.src = URL.createObjectURL(res[0].data)
} else {
videoItem.src = url
fetch(url).then(res => {
if (!res.ok) {
throw new Error('Network response was not ok ' + res.statusText);
}
return res.blob();
})
.then(blob => {
console.log(blob);
addData({ url: url, data: blob });
})
.catch(error => {
console.error('There was a problem with the fetch operation:', error);
});
}
})
}// getAllData()
</script>
</body>
</html>
node 代理处理跨域
javascript
const http = require('http');
const https = require('https');
const url = require('url');
const path = require('path');
const fs = require('fs');
// 静态资源目录
const staticDir = path.join(__dirname, 'public');
// 创建 HTTP 服务器
const server = http.createServer((req, res) => {
// 允许跨域
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
// 处理预检请求(OPTIONS)
if (req.method === 'OPTIONS') {
res.writeHead(204);
res.end();
return;
}
// 解析请求 URL
const parsedUrl = url.parse(req.url, true);
const videoUrl = parsedUrl.query.url;
// 处理视频代理请求
if (videoUrl) {
const client = videoUrl.startsWith('https') ? https : http;
client.get(videoUrl, (videoRes) => {
res.setHeader('Content-Type', videoRes.headers['content-type']);
if (videoRes.headers['content-length']) {
res.setHeader('Content-Length', videoRes.headers['content-length']);
}
videoRes.pipe(res);
}).on('error', (err) => {
console.error('Error fetching video:', err);
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Failed to fetch video' }));
});
return;
}
// 处理静态资源请求
if (req.method !== 'GET' && req.method !== 'HEAD') {
res.writeHead(405);
res.end('Method Not Allowed');
return;
}
let pathname = parsedUrl.pathname;
pathname = pathname === '/' ? '/index.html' : pathname;
// 构造安全路径
const requestedPath = path.join(staticDir, pathname);
const absolutePath = path.resolve(requestedPath);
// 路径安全检查
if (!absolutePath.startsWith(path.resolve(staticDir))) {
res.writeHead(403);
res.end('Forbidden');
return;
}
fs.stat(absolutePath, (err, stats) => {
if (err) {
res.writeHead(err.code === 'ENOENT' ? 404 : 500);
res.end();
return;
}
if (stats.isDirectory()) {
const indexPath = path.join(absolutePath, 'index.html');
fs.stat(indexPath, (err, indexStats) => {
if (!err && indexStats.isFile()) {
serveFile(indexPath, res);
} else {
res.writeHead(403);
res.end('Directory access forbidden');
}
});
} else {
serveFile(absolutePath, res);
}
});
});
// 文件服务函数
function serveFile(filePath, res) {
const ext = path.extname(filePath).toLowerCase();
const contentType = getContentType(ext);
const stream = fs.createReadStream(filePath);
res.setHeader('Content-Type', contentType);
stream.pipe(res);
stream.on('error', () => {
res.writeHead(500);
res.end('Internal Server Error');
});
}
// MIME 类型映射
function getContentType(ext) {
const mimeTypes = {
'.html': 'text/html',
'.css': 'text/css',
'.js': 'application/javascript',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.gif': 'image/gif',
'.json': 'application/json',
'.txt': 'text/plain',
};
return mimeTypes[ext] || 'application/octet-stream';
}
// 启动服务器
const PORT = 3000;
server.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
console.log(`Static files from: ${staticDir}`);
});
单独处理
javascript
const http = require('http');
const https = require('https');
const url = require('url');
// 创建 HTTP 服务器
const server = http.createServer((req, res) => {
// 允许跨域
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
// 处理预检请求(OPTIONS)
if (req.method === 'OPTIONS') {
res.writeHead(204); // No Content
res.end();
return;
}
// 解析请求 URL
const queryObject = url.parse(req.url, true).query;
const videoUrl = queryObject.url; // 从查询参数中获取视频 URL
if (!videoUrl) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Missing video URL parameter' }));
return;
}
// 根据视频 URL 的协议选择 http 或 https 模块
const client = videoUrl.startsWith('https') ? https : http;
// 发起请求到目标视频服务器
client.get(videoUrl, (videoRes) => {
// 设置响应头
res.setHeader('Content-Type', videoRes.headers['content-type']);
res.setHeader('Content-Length', videoRes.headers['content-length']);
// 流式传输视频数据
videoRes.pipe(res);
}).on('error', (err) => {
console.error('Error fetching video:', err);
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Failed to fetch video' }));
});
});
// 启动服务器
const PORT = 3000;
server.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});