
隐私100%无泄漏!Docker自建MAZANOKE免费图片压缩工具,不联网/零上传/全免费!
还在为手机、电脑里堆积如山的高清照片发愁吗?存储空间频频告急,心爱的回忆却舍不得删?更别提那些需要上传到“云端”的图片工具,总担心隐私被人悄悄“窥探”?别慌,你的救星来了!
今天给你安利一款真·神器—— MAZANOKE!它可不是普通的在线工具,而是能完全在你本地离线运行的免费图片优化专家! 直接在你的浏览器里干活,照片一张都不用外传,隐私安全锁得死死的!压缩、转换格式(JPG/PNG/WebP/HEIC/AVIF/GIF/SVG通吃)、批量处理...功能超全,关键还完全免费、开源、任意用!
最爽的是?如果你是NAS玩家,通过简单的Docker Compose一键部署,就能把MAZANOKE“请”进你的私人服务器!从此,随时随地,无论是家里还是路上,都能安全、高速地“瘦身”你的海量图片,瞬间释放TB级空间!🎉
想知道怎么在NAS上轻松拥有这个“空间魔术师+隐私守护者”?或者搭建一个自己的在线图片处理网站?赶紧往下看,详细的图文教程就在下面!👇
MAZANOKE介绍
MAZANOKE是一款免费在线本地图片压缩和转换工具,它完全在用户的浏览器中运行,支持离线运行,不需要将图片上传到外部服务器。这表示用户可以在完全隐私的前提下,轻松地对图片进行压缩和优化。
即时压缩与转换:无论是调整图片质量,设定目标文件大小,还是限制最大尺寸,都能轻松搞定。
支持多种格式:JPG、PNG、WebP、HEIC、AVIF、GIF、SVG等都手到擒来。
隐私保护:所有处理都在本地完成,你的照片从不离开你的设备。
可安装的网页应用(PWA):适合桌面、平板和移动设备使用。
官方项目体验地址
点击直接跳转:https://mazanoke.com/
部署教程
Docker Compose方式部署
演示以在群晖NAS系统上安装为例
(其他系统请自行安装好最新版本Docker、Docker Compose)打开群晖的SSH功能,使用终端软件进行连接,并切换到root状态下。
新建一个项目文件夹>右键>详细信息>常规>复制原始路径
在终端内执行命令进入群晖的docker文件夹(你想将容器数据存储到哪就进哪个文件夹)
(其他NAS系统的可直接看下一步,手动创建一个文件夹再右键文件夹查看属性,复制文件夹路径进入。)# 将/volume1/docker/mazanoke换成你自己的文件夹路径 cd /volume1/docker/mazanoke
创建
index.html
汉化网页文件(或者跳过创建,直接下载index.html后放入mazanoke文件夹)vi index.html
按字母
i
键进入编辑模式,复制并粘贴到终端,按Esc
键退出编辑模式,输入:wq
保存并退出。<!DOCTYPE html> <html class="animate-0" lang="en"> <head> <title>MAZANOKE | Online Image Optimizer That Runs Privately in Your Browser</title> <meta charset="utf-8"/> <meta content="width=device-width, initial-scale=1" name="viewport"/> <meta content="Optimize images locally and privately by converting and compressing them offline in your browser. Supports JPG, PNG, WebP, HEIC, AVIF, GIF, SVG." name="description"/> <meta content="noindex, nofollow" name="robots"/> <script> function setTheme(themeName) { localStorage.setItem('theme', themeName); document.documentElement.classList.toggle('theme-dark', themeName === 'theme-dark'); document.documentElement.classList.toggle('theme-light', themeName === 'theme-light'); setTimeout(() => document.documentElement.classList.remove('animate-0'), 300); } function toggleTheme() { document.documentElement.classList.add('animate-0'); setTheme(localStorage.getItem('theme') === 'theme-dark' ? 'theme-light' : 'theme-dark'); } (function () { const theme = localStorage.getItem('theme') || 'theme-dark'; setTheme(theme); })(); </script> <link href="./assets/css/fonts.css" rel="stylesheet" type="text/css"/> <link href="./assets/css/variables.css" rel="stylesheet" type="text/css"/> <link href="./assets/css/style.css" rel="stylesheet" type="text/css"/> <link href="./assets/images/apple-touch-icon.png" rel="apple-touch-icon" sizes="180x180"/> <link href="./assets/images/favicon-32x32.png" rel="icon" sizes="32x32" type="image/png"/> <link href="./assets/images/favicon-16x16.png" rel="icon" sizes="16x16" type="image/png"/> <link href="/manifest.json" rel="manifest"/> <meta content="#191919" name="theme-color"/> </head> <body> <div class="body-tint-bottom"></div> <nav class="top-nav"> <div class="logo-container"> <img alt="MAZANOKE logo" class="logo-image" src="./assets/images/symbol-192x192.png"/> <h1 class="logo-text">MAZANOKE</h1> </div> <div class="top-nav-actions"> <div class="theme-switch-container"> <label class="switch switch--theme" id="themeSwitch"> <input id="themeSwitchThumb" onchange="toggleTheme()" type="checkbox"/> <span class="switch-thumb"> <span class="switch-icon switch-icon--light"> <svg height="16" stroke-linejoin="round" style="color: currentcolor;" viewbox="0 0 16 16" width="16"><path clip-rule="evenodd" d="M8.75 0.75V0H7.25V0.75V2V2.75H8.75V2V0.75ZM11.182 3.75732L11.7123 3.22699L12.0659 2.87344L12.5962 2.34311L13.6569 3.40377L13.1265 3.9341L12.773 4.28765L12.2426 4.81798L11.182 3.75732ZM8 10.5C9.38071 10.5 10.5 9.38071 10.5 8C10.5 6.61929 9.38071 5.5 8 5.5C6.61929 5.5 5.5 6.61929 5.5 8C5.5 9.38071 6.61929 10.5 8 10.5ZM8 12C10.2091 12 12 10.2091 12 8C12 5.79086 10.2091 4 8 4C5.79086 4 4 5.79086 4 8C4 10.2091 5.79086 12 8 12ZM13.25 7.25H14H15.25H16V8.75H15.25H14H13.25V7.25ZM0.75 7.25H0V8.75H0.75H2H2.75V7.25H2H0.75ZM2.87348 12.0659L2.34315 12.5962L3.40381 13.6569L3.93414 13.1265L4.28769 12.773L4.81802 12.2426L3.75736 11.182L3.22703 11.7123L2.87348 12.0659ZM3.75735 4.81798L3.22702 4.28765L2.87347 3.9341L2.34314 3.40377L3.4038 2.34311L3.93413 2.87344L4.28768 3.22699L4.81802 3.75732L3.75735 4.81798ZM12.0659 13.1265L12.5962 13.6569L13.6569 12.5962L13.1265 12.0659L12.773 11.7123L12.2426 11.182L11.182 12.2426L11.7123 12.773L12.0659 13.1265ZM8.75 13.25V14V15.25V16H7.25V15.25V14V13.25H8.75Z" fill="currentColor" fill-rule="evenodd"></path></svg> </span> <span class="switch-icon switch-icon--dark"> <svg height="16" stroke-linejoin="round" style="color: currentcolor;" viewbox="0 0 16 16" width="16"><path clip-rule="evenodd" d="M1.5 8.00005C1.5 5.53089 2.99198 3.40932 5.12349 2.48889C4.88136 3.19858 4.75 3.95936 4.75 4.7501C4.75 8.61609 7.88401 11.7501 11.75 11.7501C11.8995 11.7501 12.048 11.7454 12.1953 11.7361C11.0955 13.1164 9.40047 14.0001 7.5 14.0001C4.18629 14.0001 1.5 11.3138 1.5 8.00005ZM6.41706 0.577759C2.78784 1.1031 0 4.22536 0 8.00005C0 12.1422 3.35786 15.5001 7.5 15.5001C10.5798 15.5001 13.2244 13.6438 14.3792 10.9921L13.4588 9.9797C12.9218 10.155 12.3478 10.2501 11.75 10.2501C8.71243 10.2501 6.25 7.78767 6.25 4.7501C6.25 3.63431 6.58146 2.59823 7.15111 1.73217L6.41706 0.577759ZM13.25 1V1.75V2.75L14.25 2.75H15V4.25H14.25H13.25V5.25V6H11.75V5.25V4.25H10.75L10 4.25V2.75H10.75L11.75 2.75V1.75V1H13.25Z" fill="currentColor" fill-rule="evenodd"></path></svg> </span> </span> </label> </div> <button class="button-cta button-secondary minw-auto hidden" id="installPWAPrompt" onclick="installPWADialog.showModal()"> <svg height="16" stroke-linejoin="round" style="color: currentcolor;" viewbox="0 0 16 16" width="16"><path clip-rule="evenodd" d="M8.75 5.25V4.5H7.25V5.25V9.43934L5.78033 7.96967L5.25 7.43934L4.18934 8.5L4.71967 9.03033L7.46967 11.7803C7.76256 12.0732 8.23744 12.0732 8.53033 11.7803L11.2803 9.03033L11.8107 8.5L10.75 7.43934L10.2197 7.96967L8.75 9.43934V5.25ZM1.5 8C1.5 4.41015 4.41015 1.5 8 1.5C11.5899 1.5 14.5 4.41015 14.5 8C14.5 11.5899 11.5899 14.5 8 14.5C4.41015 14.5 1.5 11.5899 1.5 8ZM8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0Z" fill="currentColor" fill-rule="evenodd"></path></svg> <span>安装</span> </button> </div> </nav> <div class="content"> <div class="drop-zone" id="compressDropZone"> <div class="drop-zone__border-dashed"></div> <div class="drop-zone__cell-grid"></div> <div class="drop-zone__vignette-outer"></div> <div class="drop-zone__vignette-inner"></div> <div class="drop-zone__shade"></div> <div class="drop-zone__shade-top"></div> <div class="drop-zone__sheen"></div> <div class="drop-zone-content"> <div class="drop-zone-content__border-top-fade"></div> <div class="drop-zone-content__border"></div> <div class="drop-zone-content__actions" id="dropZoneActions"> <div class="flex flex-col items-center gap-3xs w-100"> <svg fill="none" height="48" style="stroke: currentcolor" viewbox="0 0 24 24" width="48" xmlns="http://www.w3.org/2000/svg"><path d="M3.00005 17.0001C3 16.9355 3 16.8689 3 16.8002V7.2002C3 6.08009 3 5.51962 3.21799 5.0918C3.40973 4.71547 3.71547 4.40973 4.0918 4.21799C4.51962 4 5.08009 4 6.2002 4H17.8002C18.9203 4 19.4801 4 19.9079 4.21799C20.2842 4.40973 20.5905 4.71547 20.7822 5.0918C21 5.5192 21 6.07899 21 7.19691V16.8031C21 17.2881 21 17.6679 20.9822 17.9774M3.00005 17.0001C3.00082 17.9884 3.01337 18.5058 3.21799 18.9074C3.40973 19.2837 3.71547 19.5905 4.0918 19.7822C4.5192 20 5.07899 20 6.19691 20H17.8036C18.9215 20 19.4805 20 19.9079 19.7822C20.2842 19.5905 20.5905 19.2837 20.7822 18.9074C20.9055 18.6654 20.959 18.3813 20.9822 17.9774M3.00005 17.0001L7.76798 11.4375L7.76939 11.436C8.19227 10.9426 8.40406 10.6955 8.65527 10.6064C8.87594 10.5282 9.11686 10.53 9.33643 10.6113C9.58664 10.704 9.79506 10.9539 10.2119 11.4541L12.8831 14.6595C13.269 15.1226 13.463 15.3554 13.6986 15.4489C13.9065 15.5313 14.1357 15.5406 14.3501 15.4773C14.5942 15.4053 14.8091 15.1904 15.2388 14.7607L15.7358 14.2637C16.1733 13.8262 16.3921 13.6076 16.6397 13.5361C16.8571 13.4734 17.0896 13.4869 17.2988 13.5732C17.537 13.6716 17.7302 13.9124 18.1167 14.3955L20.9822 17.9774M20.9822 17.9774L21 17.9996M15 10C14.4477 10 14 9.55228 14 9C14 8.44772 14.4477 8 15 8C15.5523 8 16 8.44772 16 9C16 9.55228 15.5523 10 15 10Z" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.6"></path></svg> <div class="flex flex-col items-center gap-3xs w-100"> <div class="drop-zone-content__actions-title">拖放或粘贴图片</div> <div class="drop-zone-content__actions-description"><span>jpg</span><span>png</span><span>webp</span><span>heic</span><span>avif</span><span>gif</span><span>svg</span></div> </div> </div> <button class="button-cta button-secondary"> <svg height="16" stroke-linejoin="round" style="color: currentcolor;" viewbox="0 0 16 16" width="16"><path clip-rule="evenodd" d="M14.5 7.5V12.5C14.5 13.0523 14.0523 13.5 13.5 13.5H2.5C1.94772 13.5 1.5 13.0523 1.5 12.5V7.5H14.5ZM14.5 6V4H8.83333C8.29241 4 7.76607 3.82456 7.33333 3.5L6 2.5H1.5V6H14.5ZM0 1H1.5H6.16667C6.38304 1 6.59357 1.07018 6.76667 1.2L8.23333 2.3C8.40643 2.42982 8.61696 2.5 8.83333 2.5H14.5H16V4V12.5C16 13.8807 14.8807 15 13.5 15H2.5C1.11929 15 0 13.8807 0 12.5V2.5V1Z" fill="currentColor" fill-rule="evenodd"></path></svg>浏览</button> </div> <div class="progress-container hidden"> <div id="compressProgressQueueCount"></div> <div id="compressProgressTrack"> <div id="compressProgressBar"></div> </div> <div data-progress="0" id="compressProgressText"></div> <div class="mt-sm hidden" id="compressAbort"> <button class="button-cta button-secondary" onclick="abort(event)">Cancel</button> </div> </div> <input accept=".svg, .gif, .jpg, .jpeg, .png, .webp, .heic, .heif, .avif, .jxl" id="compress" multiple="" type="file"> </input></div> </div> <nav class="nav-subpage"> <div class="segmented-control-group h6" data-elevation="2" id="selectSettingsSubpage"> <div class="segmented-control segmented-control--is-selected" onclick="selectSubpage('settings')"> <div class="segmented-control__selected-item"> <!-- Selected control indicator --> </div> <div class="segmented-control__text"> <svg height="20" stroke-linejoin="round" style="color:currentColor" viewbox="0 0 16 16" width="20"><path clip-rule="evenodd" d="M10.75 5.5C11.7165 5.5 12.5 4.7165 12.5 3.75C12.5 2.7835 11.7165 2 10.75 2C9.7835 2 9 2.7835 9 3.75C9 4.7165 9.7835 5.5 10.75 5.5ZM10.75 0.75C12.1479 0.75 13.3225 1.70608 13.6555 3H15.25H16V4.5H15.25H13.6555C13.3225 5.79392 12.1479 6.75 10.75 6.75C9.35212 6.75 8.17754 5.79392 7.84451 4.5H0.75H0V3H0.75H7.84451C8.17754 1.70608 9.35212 0.75 10.75 0.75ZM15.25 13H16V11.5H15.25L8.15549 11.5C7.82245 10.2061 6.64788 9.25 5.25 9.25C3.85212 9.25 2.67755 10.2061 2.34451 11.5H0.75H0V13H0.75H2.34451C2.67755 14.2939 3.85212 15.25 5.25 15.25C6.64788 15.25 7.82246 14.2939 8.15549 13L15.25 13ZM7 12.2513C7 12.2509 7 12.2504 7 12.25C7 12.2496 7 12.2491 7 12.2487C6.99929 11.2828 6.21606 10.5 5.25 10.5C4.2835 10.5 3.5 11.2835 3.5 12.25C3.5 13.2165 4.2835 14 5.25 14C6.21606 14 6.99929 13.2172 7 12.2513Z" fill="currentColor" fill-rule="evenodd"></path></svg>设置</div> <input checked="" class="hidden" name="settingsSubpage" type="radio" value="settings"/> </div> <div class="segmented-control" data-count="0" id="subpageOutput" onclick="selectSubpage('output')"> <div class="segmented-control__text"> <svg fill="none" height="24" style="stroke: currentcolor" viewbox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M3.00005 18.0001C3 17.9355 3 17.8689 3 17.8002V6.2002C3 5.08009 3 4.51962 3.21799 4.0918C3.40973 3.71547 3.71547 3.40973 4.0918 3.21799C4.51962 3 5.08009 3 6.2002 3H17.8002C18.9203 3 19.4801 3 19.9079 3.21799C20.2842 3.40973 20.5905 3.71547 20.7822 4.0918C21 4.5192 21 5.07899 21 6.19691V17.8031C21 18.2881 21 18.6679 20.9822 18.9774M3.00005 18.0001C3.00082 18.9884 3.01337 19.5058 3.21799 19.9074C3.40973 20.2837 3.71547 20.5905 4.0918 20.7822C4.5192 21 5.07899 21 6.19691 21H17.8036C18.9215 21 19.4805 21 19.9079 20.7822C20.2842 20.5905 20.5905 20.2837 20.7822 19.9074C20.9055 19.6654 20.959 19.3813 20.9822 18.9774M3.00005 18.0001L7.76798 12.4375L7.76939 12.436C8.19227 11.9426 8.40406 11.6955 8.65527 11.6064C8.87594 11.5282 9.11686 11.53 9.33643 11.6113C9.58664 11.704 9.79506 11.9539 10.2119 12.4541L12.8831 15.6595C13.269 16.1226 13.463 16.3554 13.6986 16.4489C13.9065 16.5313 14.1357 16.5406 14.3501 16.4773C14.5942 16.4053 14.8091 16.1904 15.2388 15.7607L15.7358 15.2637C16.1733 14.8262 16.3921 14.6076 16.6397 14.5361C16.8571 14.4734 17.0896 14.4869 17.2988 14.5732C17.537 14.6716 17.7302 14.9124 18.1167 15.3955L20.9822 18.9774M20.9822 18.9774L21 18.9996M15 9C14.4477 9 14 8.55228 14 8C14 7.44772 14.4477 7 15 7C15.5523 7 16 7.44772 16 8C16 8.55228 15.5523 9 15 9Z" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.7"></path></svg> <span>图片<span class="badge"> <span class="badge-text" id="compressedImageCount"> 0 </span> </span> </span> </div> <input class="hidden" name="settingsSubpage" type="radio" value="output"/> </div> </div> </nav> <section class="image-subpage-container"> <div class="image-subpage"> <div class="image-options-container" data-elevation="2"> <div class="image-subpage-title-container"> <h2 class="h4">设置</h2> <div class="image-subpage-title-description">图像在您的设备上进行本地处理,以确保隐私。</div> </div> <div class="form-group" id="compressMethodGroup"> <div class="form-group__label-content"> <label class="form-group__label-text"> 优化方式 </label> </div> <div class="form-group__input-content"> <div class="button-card-radio-group"> <div class="button-card-radio button-card-radio--is-selected" onclick="setCompressMethod('quality')"> <div class="button-card-radio__content"> <div class="flex gap-sm w-100"> <label class="button-card-radio__label">设置图像质量</label> <div class="button-card-radio__radio-input"> <div class="button-card-radio__radio-icon"></div> <input checked="" class="hidden" name="compressMethod" type="radio" value="quality"/> </div> </div> <div class="button-card-radio__description"> <div class="button-card-radio__description-text"> 数值越大,保留的细节越多,而数值越小,文件大小越小。 </div> </div> </div> </div> <div class="button-card-radio" onclick="setCompressMethod('limitWeight')"> <div class="button-card-radio__content"> <div class="flex gap-sm w-100"> <label class="button-card-radio__label">限制文件大小</label> <div class="button-card-radio__radio-input"> <div class="button-card-radio__radio-icon"></div> <input class="hidden" name="compressMethod" type="radio" value="limitWeight"/> </div> </div> <div class="button-card-radio__description"> <div class="button-card-radio__description-text"> 将图像压缩到目标文件大小。 </div> </div> </div> </div> </div> </div> </div> <div class="form-group hidden" id="qualityField"> <div class="form-group__label-content"> <label class="form-group__label-text" for="quality"> 质量 </label> </div> <div class="form-group__input-content"> <div class="fade-in-up w-100"> <label class="mb-2xs">质量</label> <div class="flex items-center gap-sm flex-wrap"> <div class="form-field-container form-field-container--suffix"> <input id="quality" max="100" min="0" name="quality" oninput="setSlider(this.value, 'qualitySlider')" step="1" type="number" value="80"> <label class="form-field-suffix" for="quality"> % </label> </input></div> <div class="slider-track flex-1" id="qualitySlider" onmousedown="startSliderDrag(event, 'quality')"> <div class="slider-fill"></div> <div class="slider-thumb"></div> </div> </div> </div> </div> </div> <div class="form-group hidden" id="limitWeightField"> <div class="form-group__label-content"> <label class="form-group__label-text" for="maxWeight"> 目标文件大小 </label> </div> <div class="form-group__input-content"> <div class="fade-in-up w-100"> <label class="mb-2xs">目标文件大小</label> <div class="form-field-container"> <div class="flex flex-wrap gap-xs w-100"> <div class="flex-1" style="min-width: 200px"> <div class="form-field-container form-field-container--suffix w-100"> <input class="w-100" id="limitWeight" name="limitWeight" step="0.1" type="number" value="2"> <label class="form-field-suffix" data-suffix="MB" for="limitWeight">MB</label> </input></div> </div> <div class="flex-1" style="min-width: 200px"> <select class="w-100" id="limitWeightUnit"> <option value="MB">Megabytes (MB)</option> <option value="KB">Kilobytes (KB)</option> </select> </div> </div> </div> </div> </div> </div> <div class="form-group mt-lg"> <div class="form-group__label-content"> <label class="form-group__label-text" for="limitDimensions">尺寸</label> <div class="form-group__description"> </div> </div> <div class="form-group__input-content"> <div class="button-card-radio-group" id="dimensionsMethodGroup"> <div class="button-card-radio button-card-radio--is-selected" onclick="setDimensionMethod('original')"> <div class="button-card-radio__content"> <div class="flex gap-sm w-100"> <label class="button-card-radio__label">保持原始尺寸</label> <div class="button-card-radio__radio-input"> <div class="button-card-radio__radio-icon"></div> <input checked="" class="hidden" name="dimensionMethod" type="radio" value="original"/> </div> </div> <div class="button-card-radio__description"> <div class="button-card-radio__description-text"> 不要改变图像的宽度和高度。 </div> </div> </div> </div> <div class="button-card-radio" onclick="setDimensionMethod('limit')"> <div class="button-card-radio__content"> <div class="flex gap-sm w-100"> <label class="button-card-radio__label">限制尺寸</label> <div class="button-card-radio__radio-input"> <div class="button-card-radio__radio-icon"></div> <input class="hidden" name="dimensionMethod" type="radio" value="limit"/> </div> </div> <div class="button-card-radio__description"> <div class="button-card-radio__description-text"> 限制图像的宽度和高度。 </div> </div> </div> </div> </div> </div> </div> <div class="form-group hidden" id="resizeDimensionsField"> <div class="form-group__label-content"> <label class="form-group__label-text" for="limitDimensions"> <!-- Max dimension --> </label> </div> <div class="form-group__input-content"> <div class="fade-in-up w-100"> <label class="mb-2xs">限制尺寸</label> <div class="form-field-container form-field-container--suffix"> <input class="w-100" id="limitDimensions" name="limitDimensions" step="50" type="number" value="1200"> <label class="form-field-suffix" for="limitDimensions"> px </label> </input></div> </div> </div> </div> <div class="form-group mt-lg" id="formatMethodGroup"> <div class="form-group__label-content"> <label class="form-group__label-text">转换为</label> </div> <div class="form-group__input-content"> <div class="button-card-radio-group"> <div class="button-card-radio button-card-radio--is-selected" onclick="setConvertMethod('default')"> <div class="button-card-radio__content"> <div class="flex gap-sm w-100"> <label class="button-card-radio__label">默认</label> <div class="button-card-radio__radio-input"> <div class="button-card-radio__radio-icon"></div> <input checked="" class="hidden" name="formatSelect" type="radio" value="default"/> </div> </div> <div class="button-card-radio__description"> <div class="button-card-radio__description-text"> <div class="flex flex-col gap-3xs"> <div>• JPG, PNG, WebP → 不更改格式.</div> <div>• HEIC, AVIF, GIF, SVG → 转为PNG格式.</div> </div> </div> </div> </div> </div> <div class="button-card-radio" onclick="setConvertMethod('image/jpeg')"> <div class="button-card-radio__content"> <div class="flex gap-sm w-100"> <label class="button-card-radio__label">JPG</label> <div class="button-card-radio__radio-input"> <div class="button-card-radio__radio-icon"></div> <input class="hidden" name="formatSelect" type="radio" value="image/jpeg"/> </div> </div> <div class="button-card-radio__description"> <div class="button-card-radio__description-text">将所有图像转换为JPG。</div> </div> </div> </div> <div class="button-card-radio" onclick="setConvertMethod('image/png')"> <div class="button-card-radio__content"> <div class="flex gap-sm w-100"> <label class="button-card-radio__label">PNG</label> <div class="button-card-radio__radio-input"> <div class="button-card-radio__radio-icon"></div> <input class="hidden" name="formatSelect" type="radio" value="image/png"/> </div> </div> <div class="button-card-radio__description"> <div class="button-card-radio__description-text">将所有图像转换为PNG。</div> </div> </div> </div> <div class="button-card-radio" onclick="setConvertMethod('image/webp')"> <div class="button-card-radio__content"> <div class="flex gap-sm w-100"> <label class="button-card-radio__label">WebP</label> <div class="button-card-radio__radio-input"> <div class="button-card-radio__radio-icon"></div> <input class="hidden" name="formatSelect" type="radio" value="image/webp"/> </div> </div> <div class="button-card-radio__description"> <div class="button-card-radio__description-text">将所有图像转换为WebP。</div> </div> </div> </div> </div> </div> </div> </div> <div class="image-output-container" data-count="0" data-elevation="2" id="outputDownloadContainer"> <div class="image-subpage-title-container"> <div class="image-subpage-title-content"> <div class="image-subpage-title__text"> <h2 class="h4">图片</h2> <div class="image-subpage-title-description">优化后的图像可供查看和下载。</div> </div> <div class="image-output__actions-container"> <button class="button-cta button-secondary" id="deleteAllImagesButton" onclick="deleteAllImages()"> <svg height="16" stroke-linejoin="round" style="color: currentcolor;" viewbox="0 0 16 16" width="16"><path clip-rule="evenodd" d="M6.75 2.75C6.75 2.05964 7.30964 1.5 8 1.5C8.69036 1.5 9.25 2.05964 9.25 2.75V3H6.75V2.75ZM5.25 3V2.75C5.25 1.23122 6.48122 0 8 0C9.51878 0 10.75 1.23122 10.75 2.75V3H12.9201H14.25H15V4.5H14.25H13.8846L13.1776 13.6917C13.0774 14.9942 11.9913 16 10.6849 16H5.31508C4.00874 16 2.92263 14.9942 2.82244 13.6917L2.11538 4.5H1.75H1V3H1.75H3.07988H5.25ZM4.31802 13.5767L3.61982 4.5H12.3802L11.682 13.5767C11.6419 14.0977 11.2075 14.5 10.6849 14.5H5.31508C4.79254 14.5 4.3581 14.0977 4.31802 13.5767Z" fill="currentColor" fill-rule="evenodd"></path></svg> <span>全部删除</span> </button> <button aria-busy="false" class="button-cta button-primary" id="downloadAllImagesButton" onclick="downloadAllImages()"> <span class="flex aria-busy:visible"> <svg height="16" style="fill: currentcolor;" viewbox="0 0 24 24" width="16" xmlns="http://www.w3.org/2000/svg"><style>.spinner_z9k8{transform-origin:center;animation:spinner_StKS .75s infinite linear}@keyframes spinner_StKS{100%{transform:rotate(360deg)}}</style><path d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z" opacity=".25"></path><path class="spinner_z9k8" d="M12,4a8,8,0,0,1,7.89,6.7A1.53,1.53,0,0,0,21.38,12h0a1.5,1.5,0,0,0,1.48-1.75,11,11,0,0,0-21.72,0A1.5,1.5,0,0,0,2.62,12h0a1.53,1.53,0,0,0,1.49-1.3A8,8,0,0,1,12,4Z"></path></svg></span> <span class="flex aria-busy:hidden"> <svg height="16" stroke-linejoin="round" style="color: currentcolor;" viewbox="0 0 16 16" width="16"><path clip-rule="evenodd" d="M8.75 1V1.75V8.68934L10.7197 6.71967L11.25 6.18934L12.3107 7.25L11.7803 7.78033L8.70711 10.8536C8.31658 11.2441 7.68342 11.2441 7.29289 10.8536L4.21967 7.78033L3.68934 7.25L4.75 6.18934L5.28033 6.71967L7.25 8.68934V1.75V1H8.75ZM13.5 9.25V13.5H2.5V9.25V8.5H1V9.25V14C1 14.5523 1.44771 15 2 15H14C14.5523 15 15 14.5523 15 14V9.25V8.5H13.5V9.25Z" fill="currentColor" fill-rule="evenodd"></path></svg> </span> <span>全部下载</span> </button> </div> </div> </div> <div class="image-output__empty-state"> <div class="flex flex-col justify-center items-center gap-xs pt-3xl"> <svg fill="none" height="64" style="stroke: currentcolor" viewbox="0 0 24 24" width="64" xmlns="http://www.w3.org/2000/svg"><path d="M3.00005 17.0001C3 16.9355 3 16.8689 3 16.8002V7.2002C3 6.08009 3 5.51962 3.21799 5.0918C3.40973 4.71547 3.71547 4.40973 4.0918 4.21799C4.51962 4 5.08009 4 6.2002 4H17.8002C18.9203 4 19.4801 4 19.9079 4.21799C20.2842 4.40973 20.5905 4.71547 20.7822 5.0918C21 5.5192 21 6.07899 21 7.19691V16.8031C21 17.2881 21 17.6679 20.9822 17.9774M3.00005 17.0001C3.00082 17.9884 3.01337 18.5058 3.21799 18.9074C3.40973 19.2837 3.71547 19.5905 4.0918 19.7822C4.5192 20 5.07899 20 6.19691 20H17.8036C18.9215 20 19.4805 20 19.9079 19.7822C20.2842 19.5905 20.5905 19.2837 20.7822 18.9074C20.9055 18.6654 20.959 18.3813 20.9822 17.9774M3.00005 17.0001L7.76798 11.4375L7.76939 11.436C8.19227 10.9426 8.40406 10.6955 8.65527 10.6064C8.87594 10.5282 9.11686 10.53 9.33643 10.6113C9.58664 10.704 9.79506 10.9539 10.2119 11.4541L12.8831 14.6595C13.269 15.1226 13.463 15.3554 13.6986 15.4489C13.9065 15.5313 14.1357 15.5406 14.3501 15.4773C14.5942 15.4053 14.8091 15.1904 15.2388 14.7607L15.7358 14.2637C16.1733 13.8262 16.3921 13.6076 16.6397 13.5361C16.8571 13.4734 17.0896 13.4869 17.2988 13.5732C17.537 13.6716 17.7302 13.9124 18.1167 14.3955L20.9822 17.9774M20.9822 17.9774L21 17.9996M15 10C14.4477 10 14 9.55228 14 9C14 8.44772 14.4477 8 15 8C15.5523 8 16 8.44772 16 9C16 9.55228 15.5523 10 15 10Z" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.6"></path></svg> <div> <h3 class="h5">尚未优化任何图片</h3> <p>请上传图片以进行优化,压缩后会显示在此处。</p> </div> </div> </div> <div class="image-output-content" id="outputDownloadContent"></div> </div> </div> </section> </div> <dialog data-elevation="2" id="installPWADialog"> <h2 class="h4">Install MAZANOKE</h2> <div class="pl-sm pr-sm mt-md mb-md text-md" style="max-width: 360px;"> <div class="mb-sm"> <div class="flex gap-2xs"> <svg height="24" stroke-linejoin="round" style="color: currentcolor;" viewbox="0 0 16 16" width="24"><path clip-rule="evenodd" d="M8 0.154663L8.34601 0.334591L14.596 3.58459L15 3.79466V4.25V11.75V12.2053L14.596 12.4154L8.34601 15.6654L8 15.8453L7.65399 15.6654L1.40399 12.4154L1 12.2053V11.75V4.25V3.79466L1.40399 3.58459L7.65399 0.334591L8 0.154663ZM2.5 11.2947V5.44058L7.25 7.81559V13.7647L2.5 11.2947ZM8.75 13.7647L13.5 11.2947V5.44056L8.75 7.81556V13.7647ZM8 1.84534L12.5766 4.22519L7.99998 6.51352L3.42335 4.2252L8 1.84534Z" fill="currentColor" fill-rule="evenodd"></path></svg> <p>一个应用快捷方式已添加到您的设备。</p> </div> </div> <div class="mb-sm"> <div class="flex gap-2xs"> <svg height="24" stroke-linejoin="round" style="color: currentcolor;" viewbox="0 0 16 16" width="24"><path clip-rule="evenodd" d="M1 2.5C7.90356 2.5 13.5 8.09644 13.5 15H15C15 7.26801 8.73199 1 1 1V2.5ZM8 15C8 11.134 4.86599 8 1 8V6.5C5.69442 6.5 9.5 10.3056 9.5 15H8ZM2.5 15C2.5 14.1716 1.82843 13.5 1 13.5V12C2.65685 12 4 13.3431 4 15H2.5Z" fill="currentColor" fill-rule="evenodd"></path></svg> <p>即使在没有网络连接的情况下也可使用。</p> </div> </div> <div class="mb-sm"> <div class="flex gap-2xs"> <svg height="24" stroke-linejoin="round" style="color: currentcolor;" viewbox="0 0 16 16" width="24"><path clip-rule="evenodd" d="M10 4.5V6H6V4.5C6 3.39543 6.89543 2.5 8 2.5C9.10457 2.5 10 3.39543 10 4.5ZM4.5 6V4.5C4.5 2.567 6.067 1 8 1C9.933 1 11.5 2.567 11.5 4.5V6H12.5H14V7.5V12.5C14 13.8807 12.8807 15 11.5 15H4.5C3.11929 15 2 13.8807 2 12.5V7.5V6H3.5H4.5ZM11.5 7.5H10H6H4.5H3.5V12.5C3.5 13.0523 3.94772 13.5 4.5 13.5H11.5C12.0523 13.5 12.5 13.0523 12.5 12.5V7.5H11.5Z" fill="currentColor" fill-rule="evenodd"></path></svg> <p>无论在网页还是应用中,您的图像始终在本地私密处理。</p> </div> </div> </div> <div class="button-group button-group--col justify-end mt-md"> <button class="button-cta button-secondary minw-auto" onclick="installPWADialog.close()"> Cancel </button> <button class="button-cta button-primary" id="installPWA"> <svg height="16" stroke-linejoin="round" style="color: currentcolor;" viewbox="0 0 16 16" width="16"><path clip-rule="evenodd" d="M8.75 5.25V4.5H7.25V5.25V9.43934L5.78033 7.96967L5.25 7.43934L4.18934 8.5L4.71967 9.03033L7.46967 11.7803C7.76256 12.0732 8.23744 12.0732 8.53033 11.7803L11.2803 9.03033L11.8107 8.5L10.75 7.43934L10.2197 7.96967L8.75 9.43934V5.25ZM1.5 8C1.5 4.41015 4.41015 1.5 8 1.5C11.5899 1.5 14.5 4.41015 14.5 8C14.5 11.5899 11.5899 14.5 8 14.5C4.41015 14.5 1.5 11.5899 1.5 8ZM8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0Z" fill="currentColor" fill-rule="evenodd"></path></svg> <span>安装</span> </button> </div> </dialog> <div class="toast hidden" data-elevation="3" id="updateToast"> <div class="toast-content"> <div class="toast-title">有可用更新</div> <div class="toast-message">刷新页面以获取更新。</div> </div> <div class="toast-actions"> <button class="button-cta button-secondary minw-auto" onclick="updateToast.classList.add('hidden')">忽略</button> <button class="button-cta button-secondary minw-auto" id="updateToastRefreshButton">刷新</button> </div> </div> <footer> <a class="footer-text" href="https://aabcc.top" rel="noopener noreferrer" target="_blank"> <span>由 NAS技术专家,点击访问全网NAS玩法教程网:https://aabcc.top 微信:S20306 汉化本页面</span> <!-- app-version-placeholder --> </a> </footer> <button id="backToTop"> <svg height="16" stroke-linejoin="round" style="color:currentColor" viewbox="0 0 16 16" width="16"><path clip-rule="evenodd" d="M8.70711 1.39644C8.31659 1.00592 7.68342 1.00592 7.2929 1.39644L2.21968 6.46966L1.68935 6.99999L2.75001 8.06065L3.28034 7.53032L7.25001 3.56065V14.25V15H8.75001V14.25V3.56065L12.7197 7.53032L13.25 8.06065L14.3107 6.99999L13.7803 6.46966L8.70711 1.39644Z" fill="currentColor" fill-rule="evenodd"></path></svg> </button> <script src="./assets/vendor/browser-image-compression.js" type="text/javascript"></script> <script src="./assets/vendor/heic-to.js" type="text/javascript"></script> <script src="./assets/vendor/jszip.js" type="text/javascript"></script> <script src="./assets/js/global.js" type="text/javascript"></script> <script src="./assets/js/utilities.js" type="text/javascript"></script> <script src="./assets/js/helpers.js" type="text/javascript"></script> <script src="./assets/js/ui.js" type="text/javascript"></script> <script src="./assets/js/compression.js" type="text/javascript"></script> <script src="./assets/js/download.js" type="text/javascript"></script> <script src="./assets/js/events.js" type="text/javascript"></script> <script> if ('serviceWorker' in navigator) { function promptUpdate(registration) { updateToast.classList.remove('hidden'); updateToastRefreshButton.addEventListener('click', () => { if (registration.waiting) { registration.waiting.postMessage('SKIP_WAITING') } }) } if ('serviceWorker' in navigator) { window.addEventListener('load', async () => { const registration = await navigator.serviceWorker.register('/service-worker.js') if (registration.waiting) { promptUpdate(registration) } registration.addEventListener('updatefound', () => { if (registration.installing) { registration.installing.addEventListener('statechange', () => { if (registration.waiting) { if (navigator.serviceWorker.controller) { promptUpdate(registration) } else { console.log('Service Worker initialized') } } }) } }) let refreshing = false; navigator.serviceWorker.addEventListener('controllerchange', () => { if (!refreshing) { window.location.reload(); refreshing = true; } }) }) } } let deferredPrompt; window.addEventListener('beforeinstallprompt', (e) => { e.preventDefault(); deferredPrompt = e; document.getElementById('installPWAPrompt').classList.remove('hidden'); }); document.getElementById('installPWA').addEventListener('click', async () => { if (deferredPrompt) { deferredPrompt.prompt(); await deferredPrompt.userChoice; deferredPrompt = null; } }); window.addEventListener('appinstalled', (event) => { document.getElementById('installPWAPrompt').classList.add('hidden'); installPWADialog.close(); }); </script> </body> </html>
编辑docker-compose.yml模板文件
vi docker-compose.yml
按字母
i
键进入编辑模式,复制并修改下面的配置文件,粘贴到终端,按Esc
键退出编辑模式,输入:wq
保存并退出。(排版太挤的就先粘贴到文本文件内,修改后再使用,灵活应变。)
services: mazanoke: container_name: mazanoke image: ghcr.io/civilblur/mazanoke:latest ports: - "3474:80" # 左侧的3474是容器外部访问端口,可自行修改。 volumes: - ./index.html:/usr/share/nginx/html/index.html # 可以将./index.html换成完整的文件路径
执行命令,启动docker-compose模板文件,拉取镜像并创建容器。
docker-compose up -d 或 docker compose up -d
查看正在运行的项目容器
docker-compose ps 或 docker compose ps
查看正在运行的项目容器实时日志,按Ctrl+C中断查看。
docker-compose logs -f 或 docker compose logs -f
访问MAZANOKE
打开浏览器,以NAS的IP+设置的端口号进行访问。
以本机为例:http://172.16.19.200:3474/功能很简单,设置以下图片的优化方式后上传图片即可。
相关地址
GitHub项目地址:https://github.com/civilblur/mazanoke
官方项目在线使用地址:https://mazanoke.com/
文末
👇👇👇
- 感谢你赐予我前进的力量