JSON.CM
J
SON.cm
全部工具
JSON工具
Json校验格式化
Json格式化(上下)
Json格式化(左右)
Json在线压缩转义
Json生成C#实体类
Json生成Java实体类
Json生成Go结构体
SQL转Java实体类
XML和Json在线互转
Excel/CSV转Json格式
Json转Excel/CSV格式
JSON和GET参数互转
JSON转YAML
格式化转换
HTML格式化/压缩
CSS格式化/压缩
JS格式化/压缩
JS加密/解密
Python代码格式化
SQL压缩/格式化
PHP代码格式化
XML压缩/格式化
Html/JS互转
Html转义工具
Html转C#/JSP
Html转PHP代码
Html转ASP/Perl
Excel转HTML表格
Html表格生成器
HTML/MarkDown互转
正则表达式测试工具
正则生成代码
Html过滤工具
运行Js/html/css
Xpath工具
加解密编码
MD5加密工具
MD5在线解密
URL网址16进制加密
AES加密/解密
Base64加密/解密
Escape加密/解密
对称加密/解密
SHA/SHA256加密
散列/哈希加密
摩尔斯电码加解密
随机密码生成器
UUID在线生成
GUID在线生成
条形码生成器
IP/数字地址转换
图片转Base64
UTF-8转GBK
Unicode/ASCII转换
ASCII编码/解码
URL编码/解码
KeyCode键盘按键码
Android按键码
键盘测试工具
二维码生成
二维码解码
文本数字
Html在线编辑器
文章自动排版
文章内容采集
简繁字体转换
汉字转为拼音
火星文转换器
文本内容替换
文本内容对比
在线统计字数工具
内容去重工具
文字特效工具
字符串文本压缩工具
驼峰与下划线互转
全角半角转换
英文字母大小写转换
人民币大写转换工具
随机数生成器
在线北京时间
Unix时间戳在线转换
常用进制转换工具
网络
我的本机IP
IP地址查询
IP网络检测
Websocket测试
获取浏览器信息
公共DNS
各地区公共DNS
站长
域名安全检测
htaccess转nginx
生成桌面快捷方式
rem与px转换工具
在线制作ico图标
生成网页Meta标签
在线定时刷新网址
在线调色板
免费PHP虚拟主机
Layui文档与演示
网站Gzip压缩检测
网站死链检测
Whois查询工具
ICP网站备案查询
Meta标签优化分析
网页关键词密度检测
HTTP状态码查询
24小时临时邮箱
网站缩略图生成
SEO超级外链工具
计算
利率计算器在线
子网掩码计算器
在线科学计算器
日期天数计算器
周岁年龄计算器
车贷利息计算器
车贷转贷计算器
艺术
在线Photoshop
图片格式转换
在线涂鸦画板
RGB颜色在线转换
在线电子钢琴
高清壁纸下载
外站
内部购物优惠券
知己互动AI助手
Windows下载/激活
考试习题答案查询
在线图片编辑
免费高速图床
热门短剧免费看
VIP视频免费看
必应高清壁纸
网页源代码查看
GooglePlay免登录下载
全国旅游攻略
美国地址生成器
更多工具
常用User-Agent
Content-Type对照表
Request请求大全
HTTP请求头大全
HTTP状态码
ASCII对照表
HTML特殊字符转义
常见端口大全
Bootstrap字体图标
Android Manifest权限大全
Linux常用命令大全
世界各地时间
世界各国首都查询
世界各地货币查询
世界各国区号时差查询
世界节日查询
全国少数民族分布查询
中国历史朝代时间查询表
特殊符号大全
历史上的今天
本页服务由第三方{https://skyimg.net}提供,与本站无关,请谨慎对待,防止上当受骗。
{ <header> <div class="title-area"> <h1 id="page-title">Upload Home</h1> </div> <div class="top-menu"> <button id="history-button" class="history-button" title="History" aria-label="History"> <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24" style="width: 20px; height: 20px; margin-right: 5px;"> <path d="M12 0C5.372 0 0 5.372 0 12c0 6.628 5.372 12 12 12 6.628 0 12-5.372 12-12S18.628 0 12 0zm0 22c-5.52 0-10-4.48-10-10S6.48 2 12 2s10 4.48 10 10-4.48 10-10 10zm-1-15h-2v6h8v-2h-6zm1 10c-1.104 0-2 .896-2 2h2c0-1.104-.896-2-2-2z" /> </svg> </button> <button id="menu-button" class="menu-button" title="Menu" aria-label="Menu"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <line x1="3" y1="12" x2="21" y2="12"></line> <line x1="3" y1="6" x2="21" y2="6"></line> <line x1="3" y1="18" x2="21" y2="18"></line> </svg> </button> <div id="dropdown-menu" class="dropdown-menu"> <div class="menu-item"> <label for="upload-group-select">Group:</label> <select id="upload-group-select"> <option value="Ungrouped">Ungrouped</option> </select> </div> <div class="menu-item"> <label>Theme:</label> <button id="theme-toggle-button" aria-label="Toggle Theme"> <svg class="icon-sun" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <circle cx="12" cy="12" r="5"></circle> <line x1="12" y1="1" x2="12" y2="3"></line> <line x1="12" y1="21" x2="12" y2="23"></line> <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line> <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line> <line x1="1" y1="12" x2="3" y2="12"></line> <line x1="21" y1="12" x2="23" y2="12"></line> <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line> <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line> </svg> <svg class="icon-moon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M21 12.79A9 9 0 1111.21 3a7.5 7.5 0 0010.58 9.79z"></path> </svg> </button> </div> <div class="menu-item"> <label for="sync-token-input">Sync Token:</label> <input type="text" id="sync-token-input" placeholder="64-char token"> <button id="generate-sync-token" style="margin-left:5px; padding: 4px 8px; font-size: 12px;">Generate</button> </div> </div> </div> </header> <main> <p id="upload-description"></p> <div id="upload-container"> <div id="dropzone"> <span>📁</span> <input type="file" id="file-input" name="file" multiple accept="image/*"> </div> <div class="toggle-container"> <label for="convertToWebp" class="toggle-label"> Convert to WebP <input type="checkbox" id="convertToWebp" name="convertToWebp" checked> </label> <div class="link-copy-container"> <div class="link-format-container"> <select id="link-format" class="link-format-select"> <option value="url">URL</option> <option value="md">Markdown</option> <option value="bbcode">BBCode</option> </select> </div> <button id="copy-all-button" class="copy-all-button" title="Copy All Links" aria-label="Copy All Links"> 📋 </button> </div> </div> </div> <div id="preview-container"></div> </main> <div id="file-modal" class="image-modal"> <button class="close-button" aria-label="Close">×</button> <img id="modal-file" src="" alt="IMG"> </div> <div id="toast" class="toast"></div> <footer> <p> © 2024-2025 SKY Image Hosting. All rights reserved. | <a href="/api_doc" id="api-link">API</a> | <a href="/terms" id="terms-link">ToS</a> | <a href="https://stats.uptimerobot.com/5SbQMKbmTp" id="uptime-link">Uptime</a> </p> </footer> <script> const modal = document.getElementById('file-modal'); const modalFile = document.getElementById('modal-file'); const closeButton = document.querySelector('.close-button'); const dropzone = document.getElementById('dropzone'); const fileInput = document.getElementById('file-input'); const previewContainer = document.getElementById('preview-container'); const historyButton = document.getElementById('history-button'); const copyAllButton = document.getElementById('copy-all-button'); const linkFormatSelect = document.getElementById('link-format'); const toast = document.getElementById('toast'); const themeToggleButton = document.getElementById('theme-toggle-button'); const uploadDescription = document.getElementById('upload-description'); const menuButton = document.getElementById('menu-button'); const dropdownMenu = document.getElementById('dropdown-menu'); const syncTokenInput = document.getElementById('sync-token-input'); const generateSyncTokenBtn = document.getElementById('generate-sync-token'); const uploadGroupSelect = document.getElementById('upload-group-select'); menuButton.addEventListener('click', (e) => { e.stopPropagation(); dropdownMenu.classList.toggle('show'); }); window.addEventListener('click', (e) => { if (!menuButton.contains(e.target) && !dropdownMenu.contains(e.target)) { dropdownMenu.classList.remove('show'); } }); document.getElementById('page-title').addEventListener('click', () => { window.location.reload(); }); dropzone.addEventListener('dragover', (e) => { e.preventDefault(); dropzone.classList.add('active'); }); dropzone.addEventListener('dragleave', () => { dropzone.classList.remove('active'); }); dropzone.addEventListener('drop', async (e) => { e.preventDefault(); dropzone.classList.remove('active'); const files = Array.from(e.dataTransfer.files); await uploadFiles(files); }); fileInput.addEventListener('change', async () => { const files = Array.from(fileInput.files); await uploadFiles(files); }); themeToggleButton.addEventListener('click', () => { const currentTheme = document.documentElement.getAttribute('data-theme'); if (currentTheme === 'light') { document.documentElement.setAttribute('data-theme', 'dark'); localStorage.setItem('theme', 'dark'); } else { document.documentElement.setAttribute('data-theme', 'light'); localStorage.setItem('theme', 'light'); } }); window.addEventListener('DOMContentLoaded', () => { const savedTheme = localStorage.getItem('theme'); if (savedTheme === 'light') { document.documentElement.setAttribute('data-theme', 'light'); } else { document.documentElement.setAttribute('data-theme', 'dark'); } updateUploadDescription(); let token = localStorage.getItem('syncToken'); if (!token || token.length !== 64) { token = generateSyncToken(); localStorage.setItem('syncToken', token); } syncTokenInput.value = token; generateSyncTokenBtn.addEventListener('click', () => { const newToken = generateSyncToken(); localStorage.setItem('syncToken', newToken); syncTokenInput.value = newToken; showToast('New sync token generated!'); }); syncTokenInput.addEventListener('change', () => { const newToken = syncTokenInput.value.trim(); if (/^[A-Za-z0-9]{64}$/.test(newToken)) { localStorage.setItem('syncToken', newToken); showToast('Sync token updated!'); } else { showToast('Invalid token format! Must be 64 alphanumeric characters.'); syncTokenInput.value = localStorage.getItem('syncToken'); } }); loadAndSetGroups(); }); function loadAndSetGroups() { const customGroups = JSON.parse(localStorage.getItem('customGroups')) || []; const savedGroup = localStorage.getItem('selectedUploadGroup') || 'Ungrouped'; uploadGroupSelect.innerHTML = '<option value="Ungrouped">Ungrouped</option>'; customGroups.forEach(group => { const option = document.createElement('option'); option.value = group; option.textContent = group; uploadGroupSelect.appendChild(option); }); if (uploadGroupSelect.querySelector(`option[value="${savedGroup}"]`)) { uploadGroupSelect.value = savedGroup; } else { uploadGroupSelect.value = 'Ungrouped'; localStorage.setItem('selectedUploadGroup', 'Ungrouped'); } } uploadGroupSelect.addEventListener('change', () => { localStorage.setItem('selectedUploadGroup', uploadGroupSelect.value); }); function formatFileSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } function convertImageToWebP(file, quality = 0.88) { return new Promise((resolve, reject) => { const img = new Image(); const url = URL.createObjectURL(file); img.onload = () => { const canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); canvas.toBlob((blob) => { if (blob) { resolve(new File([blob], file.name.replace(/\.\w+$/, '.webp'), { type: 'image/webp' })); } else { reject(new Error('Conversion to WebP failed.')); } URL.revokeObjectURL(url); }, 'image/webp', quality); }; img.onerror = () => { reject(new Error('Failed to load image.')); URL.revokeObjectURL(url); }; img.src = url; }); } function updateUploadDescription() { let description = 'Upload Limits: Max: 99MB per file, 300MB/24h'; uploadDescription.innerHTML = description; } async function getCsrfToken() { try { const response = await fetch('/csrf-token', { method: 'GET', credentials: 'include' }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); return data.csrfToken; } catch (error) { console.error('Failed to fetch CSRF token:', error); showToast('Failed to get security token, please refresh.'); return null; } } async function uploadFiles(files) { const convertToWebpCheckbox = document.getElementById('convertToWebp'); const convertToWebp = convertToWebpCheckbox.checked; const excludeExtensions = ['svg', 'gif', 'avif', 'ico', 'webp', 'raw', 'tif', 'tiff']; const syncToken = localStorage.getItem('syncToken'); const selectedGroup = uploadGroupSelect.value; let uploadUrl = '/upload'; if (convertToWebp) { uploadUrl += '?webp=true'; } const csrfToken = await getCsrfToken(); if (!csrfToken) { return; } for (let file of files) { if (!['image/'].some(type => file.type.startsWith(type))) { continue; } if (file.size > 99 * 1024 * 1024) { showToast('File size exceeds the limit, with a maximum of 99 MB!'); continue; } const fileExtension = file.name.split('.').pop().toLowerCase(); const shouldConvert = convertToWebp && !excludeExtensions.includes(fileExtension); if (shouldConvert) { try { file = await convertImageToWebP(file); } catch (error) { showToast('Conversion to WebP failed.'); continue; } } const formData = new FormData(); formData.append('file', file); const xhr = new XMLHttpRequest(); xhr.open('POST', uploadUrl); xhr.setRequestHeader('X-CSRF-Token', csrfToken); // xhr.setRequestHeader('X-Image-Group', selectedGroup); if (syncToken && /^[A-Za-z0-9]{64}$/.test(syncToken)) { xhr.setRequestHeader('X-Sync-Token', syncToken); } const previewItem = document.createElement('div'); previewItem.classList.add('preview-item'); previewContainer.appendChild(previewItem); const img = document.createElement('img'); img.src = URL.createObjectURL(file); img.alt = 'IMG'; img.classList.add('preview-image'); img.style.display = 'none'; img.onload = () => { img.style.display = 'block'; URL.revokeObjectURL(img.src); }; img.onerror = () => img.style.display = 'none'; img.onclick = () => { if(img.dataset.fileurl) { modalFile.src = img.dataset.fileurl; modal.classList.add('active'); } }; previewItem.appendChild(img); const previewDetails = document.createElement('div'); previewDetails.classList.add('preview-details'); const imageInfo = document.createElement('div'); imageInfo.classList.add('image-info'); const imageName = document.createElement('div'); imageName.classList.add('image-name'); imageName.textContent = file.name; const imageSize = document.createElement('div'); imageSize.classList.add('image-size'); imageSize.textContent = formatFileSize(file.size); imageInfo.appendChild(imageName); imageInfo.appendChild(imageSize); previewDetails.appendChild(imageInfo); const buttonGroup = document.createElement('div'); buttonGroup.classList.add('button-group'); const copyButton = document.createElement('button'); copyButton.classList.add('copy-button'); copyButton.textContent = 'URL'; copyButton.disabled = true; copyButton.onclick = () => { if (img.dataset.fileurl) { navigator.clipboard.writeText(img.dataset.fileurl).then(() => { showToast('URL link has been copied!'); }); } }; const copyMdButton = document.createElement('button'); copyMdButton.classList.add('copy-button'); copyMdButton.textContent = 'MD'; copyMdButton.disabled = true; copyMdButton.onclick = () => { if (img.dataset.fileurl) { const markdownLink = ``; navigator.clipboard.writeText(markdownLink).then(() => { showToast('Markdown link has been copied!'); }); } }; const copyBBCButton = document.createElement('button'); copyBBCButton.classList.add('copy-button'); copyBBCButton.textContent = 'BBC'; copyBBCButton.disabled = true; copyBBCButton.onclick = () => { if (img.dataset.fileurl) { const bbcodeLink = `[img]${img.dataset.fileurl}[/img]`; navigator.clipboard.writeText(bbcodeLink).then(() => { showToast('BBCode link has been copied!'); }); } }; buttonGroup.appendChild(copyButton); buttonGroup.appendChild(copyMdButton); buttonGroup.appendChild(copyBBCButton); previewDetails.appendChild(buttonGroup); previewItem.appendChild(previewDetails); const progressContainer = document.createElement('div'); progressContainer.classList.add('progress-container'); const progressCircle = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); progressCircle.setAttribute('class', 'progress-circle'); progressCircle.setAttribute('viewBox', '0 0 60 60'); const backgroundCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); backgroundCircle.setAttribute('class', 'progress-background'); backgroundCircle.setAttribute('cx', '30'); backgroundCircle.setAttribute('cy', '30'); backgroundCircle.setAttribute('r', '27'); const progressBar = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); progressBar.setAttribute('class', 'progress-bar'); progressBar.setAttribute('cx', '30'); progressBar.setAttribute('cy', '30'); progressBar.setAttribute('r', '27'); progressBar.setAttribute('stroke-dasharray', `${2 * Math.PI * 27}`); progressBar.setAttribute('stroke-dashoffset', `${2 * Math.PI * 27}`); progressCircle.appendChild(backgroundCircle); progressCircle.appendChild(progressBar); const progressText = document.createElement('div'); progressText.classList.add('progress-text'); progressText.textContent = '0%'; progressContainer.appendChild(progressCircle); progressContainer.appendChild(progressText); previewItem.appendChild(progressContainer); xhr.upload.onprogress = (event) => { if (event.lengthComputable) { const percentComplete = Math.round((event.loaded / event.total) * 100); const offset = 2 * Math.PI * 27 * (1 - percentComplete / 100); progressBar.setAttribute('stroke-dashoffset', offset); progressText.textContent = `${percentComplete}%`; } }; xhr.onload = async () => { progressContainer.style.display = 'none'; if (xhr.status === 200) { const result = JSON.parse(xhr.responseText); result.forEach(fileData => { img.src = fileData.thumbnail ? fileData.thumbnail : fileData.url; img.dataset.fileurl = fileData.url; img.alt = 'IMG'; img.style.display = 'block'; img.onerror = () => img.style.display = 'none'; copyButton.disabled = false; copyMdButton.disabled = false; copyBBCButton.disabled = false; saveToHistory(fileData.url, fileData.thumbnail, fileData.identifier, selectedGroup); }); } else if (xhr.status === 429) { showToast('You have reached your upload limit. You can upload again in 24 hour.'); progressBar.style.stroke = '#e53e3e'; progressText.textContent = 'Limit'; previewItem.style.borderColor = '#e53e3e'; } else { showToast('An error occurred while uploading the file.'); progressBar.style.stroke = '#e53e3e'; progressText.textContent = 'Error'; previewItem.style.borderColor = '#e53e3e'; } }; xhr.onerror = () => { progressContainer.style.display = 'none'; showToast('Upload failed due to a network error.'); progressBar.style.stroke = '#e53e3e'; progressText.textContent = 'Error'; previewItem.style.borderColor = '#e53e3e'; }; xhr.send(formData); } } copyAllButton.addEventListener('click', () => { const links = Array.from(previewContainer.children).map(item => { const img = item.querySelector('img'); if(!img || !img.dataset.fileurl) return ''; const url = img.dataset.fileurl; switch (linkFormatSelect.value) { case 'md': return ``; case 'bbcode': return `[img]${url}[/img]`; default: return url; } }).filter(l => l !== ''); if (links.length > 0) { navigator.clipboard.writeText(links.join('\n')).then(() => { showToast('All links have been copied!'); }); } else { showToast('No links available to copy.'); } }); function saveToHistory(url, thumbnail, identifier, group) { let history = JSON.parse(localStorage.getItem('imageHistory')) || []; if (!history.some(item => item.url === url)) { history.push({ url: url, thumbnail: thumbnail, identifier: identifier, group: group }); localStorage.setItem('imageHistory', JSON.stringify(history)); } } historyButton.addEventListener('click', () => { window.location.href = '/history'; }); closeButton.addEventListener('click', () => { modal.classList.remove('active'); }); window.addEventListener('click', (e) => { if (e.target === modal) { modal.classList.remove('active'); } }); window.addEventListener('paste', async (e) => { if (e.clipboardData && e.clipboardData.items) { const items = e.clipboardData.items; let filesToUpload = []; for (const item of items) { if (item.kind === 'file' && item.type.startsWith('image/')) { const file = item.getAsFile(); if (file) { filesToUpload.push(file); } } } if (filesToUpload.length > 0) { await uploadFiles(filesToUpload); } } }); function showToast(message) { const toast = document.getElementById('toast'); const userLang = navigator.language || navigator.userLanguage; let displayMessage = message; if (userLang && userLang.startsWith('zh')) { displayMessage = translateMessageToChinese(message); } toast.textContent = displayMessage; toast.classList.add('show'); setTimeout(() => { toast.classList.remove('show'); }, 3000); } function translateMessageToChinese(message) { switch (message) { case 'File size exceeds the limit, with a maximum of 99 MB!': return '文件大小超过限制,最大为 99 MB}!'; case 'URL link has been copied!': return 'URL 链接已复制!'; case 'Conversion to WebP failed.': return '转换为 WebP 失败。'; case 'Failed to load image.': return '加载图片失败。'; case 'Markdown link has been copied!': return 'Markdown 链接已复制!'; case 'BBCode link has been copied!': return 'BBCode 链接已复制!'; case 'You have reached your upload limit. You can upload again in 24 hour.': return '您的上传限制已达到。您可以在二十四小时后再次上传。'; case 'An error occurred while uploading the file.': return '上传文件时出错。'; case 'Upload failed due to a network error.': return '上传因网络错误失败。'; case 'All links have been copied!': return '所有链接已复制!'; case 'No links available to copy.': return '没有可复制的链接。'; case 'New sync token generated!': return '已生成新的同步令牌!'; case 'Sync token updated!': return '同步令牌已更新!'; case 'Invalid token format! Must be 64 alphanumeric characters.': return '无效的令牌格式!必须是64个字母数字字符。'; case 'Failed to get security token, please refresh.': return '获取安全令牌失败,请刷新页面。'; default: return message; } } function generateSyncToken() { const array = new Uint8Array(32); window.crypto.getRandomValues(array); return Array.from(array, byte => ('0' + byte.toString(16)).slice(-2)).join(''); } </script> <script src="notice.js"></script> }
您的足迹:
友情链接:
JSON格式化
域名一口价
域名综合查询
中国风吉网
每日读句子
内部优惠券
站长工具箱
我的IP地址
Copyright ©2022-2025
JSON.CM
All rights reserved