PageSourceSearch

https://liuxy0551.github.io/article/immich-docker.html

html liuxy0551.github.io collected 2026-10-03 09:31:24 UTC 66,032 bytes, 2,039 lines download raw bytes

1<!DOCTYPE html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15<html class="theme-next gemini" lang="zh-CN">
16<head><meta name="generator" content="Hexo 3.9.0">
17<script src="/js/src/photoswipe.min.js?v="></script>
vendor: 1 bytes, line 17
17
18<script src="/js/src/photoswipe-ui-default.min.js?v="></script>
18
19
20  <meta charset="UTF-8">
21<meta name="google-site-verification" content="xB9qCNYskF7D5dubQn6GwpuB9UX6ZULjI7A9rO7oD-4">
22<meta name="google-site-verification" content="53nL4S-QcXHb24jISzRDs8T0P7ytedH6xyGv_lqZu10">
23<meta name="baidu-site-verification" content="x0Z9lwLVom">
24<meta http-equiv="X-UA-Compatible" content="IE=edge">
25<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
26<meta name="theme-color" content="#222">
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47  
48  
49    
50  
51  <link rel="stylesheet" href="//cdn.jsdelivr.net/gh/fancyapps/fancybox@3/dist/jquery.fancybox.min.css">
52
53
54
55
56  
57  
58  
59  
60
61  
62
63  
64
65  
66
67  
68
69  
70    
71
72    
73  
74
75  
76    
77    
78    <link rel="stylesheet" href="//fonts.loli.net/css?family=PT Mono:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext">
79  
80
81
82
83
84
85
86  
87
88<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
89
90<link rel="stylesheet" href="/css/main.css?v=7.0.1">
91
92
93  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=7.0.1">
94
95
96  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon.png?v=7.0.1">
97
98
99  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon.png?v=7.0.1">
100
101
102  <link rel="mask-icon" href="/images/logo.svg?v=7.0.1" color="#222">
103
104
105
106
107
108
109
110<script id="hexo.configurations">
111  var NexT = window.NexT || {};
112  var CONFIG = {
113    root: '/',
114    scheme: 'Gemini',
115    version: '7.0.1',
116    sidebar: {"position":"left","display":"post","offset":12,"onmobile":false,"dimmer":false},
117    back2top: true,
118    back2top_sidebar: true,
119    fancybox: true,
120    fastclick: true,
121    lazyload: true,
122    tabs: true,
123    motion: {"enable":false,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
124    algolia: {
125      applicationID: '',
126      apiKey: '',
127      indexName: '',
128      hits: {"per_page":10},
129      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
130    }
131  };
132</script>
132
133
134
135  
136
137
138
139
140
141
142
143  <meta name="description" content="最早之前使用的是百度网盘备份视频和照片,近两年用的是不怎么限速的阿里网盘。近期想从百度网盘下载点东西,限速后太过费劲,所以准备弃用,开了个 SVIP,将所有文件全部下载到了硬盘。同理,担心后续阿里网盘限速,虽然视频照片这类资源备份后基本不怎么再次下载,但本着我不下载但你不能限速的原则,找到了一个开源的相册备份应用 immich。">
144<meta name="keywords" content="Linux">
145<meta property="og:type" content="article">
146<meta property="og:title" content="53k star! 高颜值的开源相册工具 —— immich">
147<meta property="og:url" content="https://liuxianyu.cn/article/immich-docker.html">
148<meta property="og:site_name" content="刘先玉">
149<meta property="og:description" content="最早之前使用的是百度网盘备份视频和照片,近两年用的是不怎么限速的阿里网盘。近期想从百度网盘下载点东西,限速后太过费劲,所以准备弃用,开了个 SVIP,将所有文件全部下载到了硬盘。同理,担心后续阿里网盘限速,虽然视频照片这类资源备份后基本不怎么再次下载,但本着我不下载但你不能限速的原则,找到了一个开源的相册备份应用 immich。">
150<meta property="og:locale" content="zh-CN">
151<meta property="og:image" content="https://images-hosting.liuxianyu.cn/posts/immich-docker/1.png">
152<meta property="og:image" content="https://images-hosting.liuxianyu.cn/posts/immich-docker/2.png">
153<meta property="og:image" content="https://images-hosting.liuxianyu.cn/posts/immich-docker/3.png">
154<meta property="og:image" content="https://images-hosting.liuxianyu.cn/posts/immich-docker/4.png">
155<meta property="og:image" content="https://images-hosting.liuxianyu.cn/posts/immich-docker/5.png">
156<meta property="og:image" content="https://images-hosting.liuxianyu.cn/posts/immich-docker/6.png">
157<meta property="og:updated_time" content="2024-11-26T09:42:38.000Z">
158<meta name="twitter:card" content="summary">
159<meta name="twitter:title" content="53k star! 高颜值的开源相册工具 —— immich">
160<meta name="twitter:description" content="最早之前使用的是百度网盘备份视频和照片,近两年用的是不怎么限速的阿里网盘。近期想从百度网盘下载点东西,限速后太过费劲,所以准备弃用,开了个 SVIP,将所有文件全部下载到了硬盘。同理,担心后续阿里网盘限速,虽然视频照片这类资源备份后基本不怎么再次下载,但本着我不下载但你不能限速的原则,找到了一个开源的相册备份应用 immich。">
161<meta name="twitter:image" content="https://images-hosting.liuxianyu.cn/posts/immich-docker/1.png">
162
163
164
165  <link rel="alternate" href="/atom.xml" title="刘先玉" type="application/atom+xml">
166
167
168
169
170  <link rel="canonical" href="https://liuxianyu.cn/article/immich-docker.html">
171
172
173
174<script id="page.configurations">
175  CONFIG.page = {
176    sidebar: "",
177  };
178</script>
178
179
180<script>
181    (function(){
182        if(''){
183            if (prompt('请输入查看密码') !== ''){
184                history.back();
185                alert('密码不正确,请再想想');
186            }
187        }
188    })();
189</script>
189
190
191  <title>53k star! 高颜值的开源相册工具 —— immich | 刘先玉</title>
192  
193
194
195
196
197
198
199  
199<script>
200    var _hmt = _hmt || [];
201    (function() {
202      var hm = document.createElement("script");
203      hm.src = "https://hm.baidu.com/hm.js?d1692b861c9c27f34c82f3b5fb2566d0";
204      var s = document.getElementsByTagName("script")[0];
205      s.parentNode.insertBefore(hm, s);
206    })();
207  </script>
207
208
209
210
211
212
213
214
215  <noscript>
216  <style>
217  .use-motion .motion-element,
218  .use-motion .brand,
219  .use-motion .menu-item,
220  .sidebar-inner,
221  .use-motion .post-block,
222  .use-motion .pagination,
223  .use-motion .comments,
224  .use-motion .post-header,
225  .use-motion .post-body,
226  .use-motion .collection-title { opacity: initial; }
227
228  .use-motion .logo,
229  .use-motion .site-title,
230  .use-motion .site-subtitle {
231    opacity: initial;
232    top: initial;
233  }
234
235  .use-motion .logo-line-before i { left: initial; }
236  .use-motion .logo-line-after i { right: initial; }
237  </style>
238</noscript>
239
240
241</head>
242
243<body itemscope itemtype="https://schema.org/WebPage" lang="zh-CN">
244
245  
246<style>
247#selectionCopyright {
248    position: absolute;
249    display: none;
250    background: rgba(244,67,54,.7);
251    color: #fff;
252    border-radius: 6px;
253    box-shadow: none;
254    border: none;
255    font-size: 14px;
256}
257#selectionCopyright a{
258    color:#fff;
259    border-color: #fff;
260}
261#selectionCopyright::before {
262    content: "";
263    width: 0;
264    height: 0;
265    border-style: solid;
266    border-width: 6px 8px 6px 0;
267    border-color: transparent rgba(244,67,54,.7) transparent transparent;
268    position: absolute;
269    left: -8px;
270    top:50%;
271    transform:translateY(-50%);
272}
273</style>
274
275
276<!-- <button id="selectionCopyright" disabled="disabled">本文发表于 [<a href="https://liuxianyu.cn">刘先玉的个人随笔</a>] 分享记得注明来源哟</button> -->
277
278
279<script>
280window.onload = function() {
281    function selectText() {
282        if (document.selection) { //IE浏览器下
283            return document.selection.createRange().text; //返回选中的文字
284        } else { //非IE浏览器下
285            return window.getSelection().toString(); //返回选中的文字
286        }
287    }
288    var content = document.getElementsByTagName("body")[0];
289    var scTip = document.getElementById('selectionCopyright');
290
291    content.onmouseup = function(ev) { //设定一个onmouseup事件
292        var ev = ev || window.event;
293        var left = ev.clientX;//获取鼠标相对浏览器可视区域左上角水平距离距离
294        var top = ev.clientY;//获取鼠标相对浏览器可视区域左上角垂直距离距离
295        var xScroll = Math.max(document.body.scrollLeft, document.documentElement.scrollLeft);//获取文档水平滚动距离
296        var yScroll = Math.max(document.body.scrollTop, document.documentElement.scrollTop);//获取文档垂直滚动距离
297        if (selectText().length > 0 && scTip) {
298            setTimeout(function() { //设定一个定时器
299                scTip.style.display = 'inline-block';
300                scTip.style.left = left + xScroll + 15 + 'px';//鼠标当前x值
301                scTip.style.top = top + yScroll - 15 + 'px';//鼠标当前y值
302            }, 100);
303        } else {
304            scTip && (scTip.style.display = 'none');
305        }
306    };
307
308    content.onclick = function(ev) {
309        var ev = ev || window.event;
310        ev.cancelBubble = true;
311    };
312    document.onclick = function() {
313        scTip.style.display = 'none';
314    };
315};
316</script>
316
317
318
319
320  
321  
322    
323  
324
325  <div class="container sidebar-position-left page-post-detail">
326    <div class="headband"></div>
327    <a href="https://github.com/liuxy0551" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewbox="0 0 250 250" style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"/><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"/><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"/></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
328
329    
330
331    <header id="header" class="header" itemscope itemtype="https://schema.org/WPHeader">
332      <div class="header-inner"><div class="site-brand-wrapper">
333  <div class="site-meta">
334    
335
336    <div class="custom-logo-site-title">
337      <a href="/" class="brand" rel="start">
338        <span class="logo-line-before"><i></i></span>
339        <span class="site-title">刘先玉</span>
340        <span class="logo-line-after"><i></i></span>
341      </a>
342    </div>
343    
344      
345        <h1 class="site-subtitle" itemprop="description">知易行难</h1>
346      
347    
348    
349  </div>
350
351  <div class="site-nav-toggle">
352    <button aria-label="切换导航栏">
353      <span class="btn-bar"></span>
354      <span class="btn-bar"></span>
355      <span class="btn-bar"></span>
356    </button>
357  </div>
358</div>
359
360
361
362<nav class="site-nav">
363  
364    <ul id="menu" class="menu">
365      
366        
367        
368        
369          
370          <li class="menu-item menu-item-home">
371
372    
373    
374    
375      
376    
377
378    
379      
380    
381
382    <a href="/" rel="section"><i class="menu-item-icon fa fa-fw fa-home"></i> <br>首页</a>
383
384  </li>
385        
386        
387        
388          
389          <li class="menu-item menu-item-tags">
390
391    
392    
393    
394      
395    
396
397    
398      
399    
400
401    <a href="/tags/" rel="section"><i class="menu-item-icon fa fa-fw fa-tags"></i> <br>标签<span class="badge">56</span></a>
402
403  </li>
404        
405        
406        
407          
408          <li class="menu-item menu-item-categories">
409
410    
411    
412    
413      
414    
415
416    
417      
418    
419
420    <a href="/categories/" rel="section"><i class="menu-item-icon fa fa-fw fa-th"></i> <br>分类<span class="badge">63</span></a>
421
422  </li>
423        
424        
425        
426          
427          <li class="menu-item menu-item-archives">
428
429    
430    
431    
432      
433    
434
435    
436      
437    
438
439    <a href="/archives/" rel="section"><i class="menu-item-icon fa fa-fw fa-archive"></i> <br>归档<span class="badge">199</span></a>
440
441  </li>
442        
443        
444        
445          
446          <li class="menu-item menu-item-links">
447
448    
449    
450    
451      
452    
453
454    
455      
456    
457
458    <a href="/links/" rel="section"><i class="menu-item-icon fa fa-fw fa-paper-plane"></i> <br>友链</a>
459
460  </li>
461        
462        
463        
464          
465          <li class="menu-item menu-item-about">
466
467    
468    
469    
470      
471    
472
473    
474      
475    
476
477    <a href="/about/" rel="section"><i class="menu-item-icon fa fa-fw fa-user"></i> <br>关于</a>
478
479  </li>
480
481      
482      
483        <li class="menu-item menu-item-search">
484          
485            <a href="javascript:;" class="popup-trigger">
486          
487            
488              <i class="menu-item-icon fa fa-search fa-fw"></i> <br>搜索</a>
489        </li>
490      
491    </ul>
492  
493
494  
495
496  
497    <div class="site-search">
498      
499  <div class="popup search-popup local-search-popup">
500  <div class="local-search-header clearfix">
501    <span class="search-icon">
502      <i class="fa fa-search"></i>
503    </span>
504    <span class="popup-btn-close">
505      <i class="fa fa-times-circle"></i>
506    </span>
507    <div class="local-search-input-wrapper">
508      <input autocomplete="off" placeholder="搜索..." spellcheck="false" type="text" id="local-search-input">
509    </div>
510  </div>
511  <div id="local-search-result"></div>
512</div>
513
514
515
516    </div>
517  
518</nav>
519
520
521
522  
523
524
525
526</div>
527    </header>
528
529    
530
531
532    <main id="main" class="main">
533      <div class="main-inner">
534        <div class="content-wrap">
535          
536            
537
538          
539          <div id="content" class="content">
540            
541
542  <div id="posts" class="posts-expand">
543    
544
545  
546
547  
548  
549  
550
551  
552
553  <article class="post post-type-normal" itemscope itemtype="https://schema.org/Article">
554  
555  
556  
557  <div class="post-block">
558    <link itemprop="mainEntityOfPage" href="https://liuxianyu.cn/article/immich-docker.html">
559
560    <span hidden itemprop="author" itemscope itemtype="https://schema.org/Person">
561      <meta itemprop="name" content="liuxy0551">
562      <meta itemprop="description" content="Easier said than done">
563      <meta itemprop="image" content="/images/avatar.jpg">
564    </span>
565
566    <span hidden itemprop="publisher" itemscope itemtype="https://schema.org/Organization">
567      <meta itemprop="name" content="刘先玉">
568    </span>
569
570    
571      <header class="post-header">
572
573        
574        
575          <h2 class="post-title" itemprop="name headline">53k star! 高颜值的开源相册工具 —— immich
576
577              
578            
579          </h2>
580        
581
582        <div class="post-meta">
583          <span class="post-time">
584            
585            
586            
587            
588
589            
590              <span class="post-meta-item-icon">
591                <i class="fa fa-calendar-o"></i>
592              </span>
593              
594                <span class="post-meta-item-text">发表于</span>
595              
596
597              
598                
599              
600
601              <time title="创建时间:2024-11-26 09:42:38" itemprop="dateCreated datePublished" datetime="2024-11-26T09:42:38Z">2024-11-26</time>
602            
603
604            
605          </span>
606
607          
608            <span class="post-category">
609            
610              <span class="post-meta-divider">|</span>
611            
612              <span class="post-meta-item-icon">
613                <i class="fa fa-folder-o"></i>
614              </span>
615              
616                <span class="post-meta-item-text">分类于</span>
617              
618              
619                <span itemprop="about" itemscope itemtype="https://schema.org/Thing"><a href="/categories/Linux/" itemprop="url" rel="index"><span itemprop="name">Linux</span></a></span>
620
621                
622                
623              
624            </span>
625          
626
627          
628            
629            
630              
631              <span class="post-comments-count">
632                <span class="post-meta-divider">|</span>
633                <span class="post-meta-item-icon">
634                  <i class="fa fa-comment-o"></i>
635                </span>
636            
637                <span class="post-meta-item-text">评论数:</span>
638                <a href="/article/immich-docker.html#comments" itemprop="discussionUrl">
639                  <span class="post-comments-count valine-comment-count" data-xid="/article/immich-docker.html" itemprop="commentCount"></span>
640                </a>
641              </span>
642            
643          
644
645          
646          
647            <span id="/article/immich-docker.html" class="leancloud_visitors" data-flag-title="53k star! 高颜值的开源相册工具 —— immich">
648              <span class="post-meta-divider">|</span>
649              <span class="post-meta-item-icon">
650                <i class="fa fa-eye"></i>
651              </span>
652              
653                <span class="post-meta-item-text">热度:</span>
654              
655                <span class="leancloud-visitors-count"></span><span>℃</span>
656            </span>
657          
658
659          
660            <span class="post-meta-divider">|</span>
661            <span class="post-meta-item-icon">
662            <i class="fa fa-eye"></i>
663             热度: 
664            <span class="busuanzi-value" id="busuanzi_value_page_pv"></span><span> ℃</span>
665            </span>
666          
667
668          
669
670          
671
672        </div>
673      </header>
674    
675
676    
677    
678    
679    <div class="post-body han-init-context" itemprop="articleBody">
680
681      
682      
683
684      
685        <p>  最早之前使用的是百度网盘备份视频和照片,近两年用的是不怎么限速的阿里网盘。近期想从百度网盘下载点东西,限速后太过费劲,所以准备弃用,开了个 SVIP,将所有文件全部下载到了硬盘。同理,担心后续阿里网盘限速,虽然视频照片这类资源备份后基本不怎么再次下载,但本着我不下载但你不能限速的原则,找到了一个开源的相册备份应用 <code>immich</code>。</p>
686<a id="more"></a>
687<p>  也对比过一些其他的开源软件,比如 NextCloud,但是个人觉得
687还是 immich 更好用。最近用了一段时间,体验还不错,内网下上传照片极快,也有对应手机平台的 APP 支持,体验较好。这里记录下通过 docker compose 安装 immich 的过程和一些改动。</p>
688<h3 id="一-环境与链接"><a class="header-anchor" href="#一-环境与链接">¶</a>一、环境与链接</h3>
689<p>  <strong>最好能有科学上网</strong>,<a href="https://liuxianyu.cn/article/http-proxy.html">远程服务器使用本地代理</a></p>
690<ul>
691<li>硬件:中柏 n100 pro Ⅱ 16G + 512G</li>
692<li>系统:Ubuntu 24.04</li>
693<li>官网:<a href="https://immich.app/" target="_blank" rel="noopener">https://immich.app/</a></li>
694</ul>
695<h3 id="二-安装"><a class="header-anchor" href="#二-安装">¶</a>二、安装</h3>
696<h4 id="2-1-安装-docker"><a class="header-anchor" href="#2-1-安装-docker">¶</a>2.1 安装 docker</h4>
697<p>  我是通过 docker compose 安装的,简单记录下如何安装 docker compose:</p>
698<figure class="highlight shell"><figcaption><span>安装 docker</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> Add Docker<span class="string">'s official GPG key:</span></span></span><br><span class="line">sudo apt update -y</span><br><span class="line">sudo apt install ca-certificates curl -y</span><br><span class="line">sudo install -m 0755 -d /etc/apt/keyrings</span><br><span class="line">sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc</span><br><span class="line">sudo chmod a+r /etc/apt/keyrings/docker.asc</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> Add the repository to Apt sources:</span></span><br><span class="line">echo \</span><br><span class="line">  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \</span><br><span class="line"><span class="meta">  $</span><span class="bash">(. /etc/os-release &amp;&amp; <span class="built_in">echo</span> <span class="string">"<span class="variable">$VERSION_CODENAME</span>"</span>) stable<span class="string">" | \</span></span></span><br><span class="line">  sudo tee /etc/apt/sources.list.d/docker.list &gt; /dev/null</span><br><span class="line">sudo apt update -y</span><br><span class="line">sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y</span><br><span class="line">docker -v</span><br></pre></td></tr></table></figure>
699<figure class="highlight shell"><figcaption><span>安装 docker compose</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">sudo apt update -y</span><br><span class="line">sudo apt install docker-compose-plugin -y</span><br><span class="line">docker compose version</span><br></pre></td></tr></table></figure>
700<h4 id="2-2-docker-compose-yml"><a class="header-anchor" href="#2-2-docker-compose-yml">¶</a>2.2 docker-compose.yml</h4>
701<figure class="highlight shell"><figcaption><span>存储相关文件</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mkdir -p /mnt/docker/immich-app</span><br><span class="line">cd /mnt/docker/immich-app</span><br></pre></td></tr></table></figure>
702<p>  官网的安装步骤在这里:<a href="https://immich.app/docs/install/docker-compose/%E3%80%82" target="_blank" rel="noopener">https://immich.app/docs/install/docker-compose/。</a> 下载 <code>docker-compose.yml</code> 和配置文件 <code>.env</code>,如果有硬件加速的能力(N 卡之类的硬件)可以点开官网文档查看,这里不进行。</p>
703<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml</span><br><span class="line">wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.env</span><br></pre></td></tr></table></figure>
704<p>  <code>vim .env</code> 追加时区:<code>TZ=Asia/Shanghai</code></p>
705<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker compose -p immich up -d</span><br></pre></td></tr></table></figure>
706<p>  稍等片刻即可访问,默认端口是 <code>2283</code>。我这里是在局域网其他电脑访问的:<a href="http://192.168.31.101:2283" target="_blank" rel="noopener">http://192.168.31.101:2283</a>。第一次进入会有一些初始化的设置。</p>
707<h4 id="2-3-大模型"><a class="header-anchor" href="#2-3-大模型">¶</a>2.3 大模型</h4>
708<p>  immich 强大在可以支持大模型处理,能够标注人脸和按文字搜索照片。上传一定量图片后,如果左侧 探索 功能中还是没有人脸,可能是由于服务器端存在网络问题,此时需要在服务器端准备好大模型。在 <code>docker-compose.yaml</code> 文件同级新建 <code>model_cache</code> 文件夹,然后执行:</p>
709<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cd /mnt/docker/immich-app</span><br><span class="line">mkdir -p model-cache/clip model-cache/facial-recognition</span><br></pre></td></tr></table></figure>
710<p>  这里我们会用到两个大模型 <a href="https://huggingface.co/immich-app/XLM-Roberta-Large-Vit-B-16Plus/tree/main" target="_blank" rel="noopener">XLM-Roberta-Large-Vit-B-16Plus</a> 和 <a href="https://huggingface.co/immich-app/buffalo_l/tree/main" target="_blank" rel="noopener">buffalo_l</a>,分布用于中文搜索(以文搜图)和人脸识别。用到的的相关文件我都放到了 <a href="https://pan.quark.cn/s/f623f75acd2a" target="_blank" rel="noopener">夸克云盘 immich-docker</a>
710,也可以自行下载。</p>
711<p>  我们需要分别下载并解压到 <code>model-cache/clip</code> 和 <code>model-cache/facial-recognition</code> 目录下。因为仓库文件较大,需要借助 <a href="https://github.com/git-lfs/git-lfs/releases" target="_blank" rel="noopener">git-fls</a>:</p>
712<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">tar -zxvf git-lfs-linux-amd64-v3.6.0.tar.gz</span><br><span class="line">cd git-lfs-3.6.0</span><br><span class="line">sudo ./install.sh</span><br><span class="line">git lfs install</span><br></pre></td></tr></table></figure>
713<blockquote>
714<p><strong>注意:</strong> 大模型的路径是有讲究的。</p>
715</blockquote>
716<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cd /mnt/docker/immich-app/model-cache/clip</span><br><span class="line">git clone https://huggingface.co/immich-app/XLM-Roberta-Large-Vit-B-16Plus</span><br></pre></td></tr></table></figure>
717<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cd /mnt/docker/immich-app/model-cache/facial-recognition</span><br><span class="line">git clone https://huggingface.co/immich-app/buffalo_l</span><br></pre></td></tr></table></figure>
718<p><img src="https://images-hosting.liuxianyu.cn/posts/immich-docker/1.png" alt></p>
719<p>   <code>vim .env</code> 定义大模型的路径:</p>
720<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># model cache path</span><br><span class="line">MODEL_CACHE=./model-cache</span><br></pre></td></tr></table></figure>
721<p>  <code>vim docker-compose.yml</code> 使用大模型的路径:</p>
722<p><img src="https://images-hosting.liuxianyu.cn/posts/immich-docker/2.png" alt></p>
723<p>  分别使用两个大模型,然后 <a href="https://liuxianyu.cn/article/immich-docker.html#2-4-%E9%87%8D%E5%90%AF-immich">在服务器端重启 immich</a>。</p>
724<p><img src="https://images-hosting.liuxianyu.cn/posts/immich-docker/3.png" alt><br>
725<img src="https://images-hosting.liuxianyu.cn/posts/immich-docker/4.png" alt></p>
726<h4 id="2-4-重启-immich"><a class="header-anchor" href="#2-4-重启-immich">¶</a>2.4 重启 immich</h4>
727<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">docker compose down</span><br><span class="line">docker compose -p immich up -d</span><br></pre></td></tr></table></figure>
728<p>  重启完成后,重新拉起相关的任务。</p>
729<p><img src="https://images-hosting.liuxianyu.cn/posts/immich-docker/5.png" alt></p>
730<p>  等待运行完成,搜索功能就有人脸分组了。</p>
731<p><img src="https://images-hosting.liuxianyu.cn/posts/immich-docker/6.png" alt></p>
732<h4 id="2-5-更新-immich"><a class="header-anchor" href="#2-5-更新-immich">¶</a>2.5 更新 immich</h4>
733<p>  进入 immich 时如果有更新提示,可以在服务器端执行:</p>
734<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker compose pull &amp;&amp; docker compose -p immich up -d</span><br></pre></td></tr></table></figure>
735<h3 id="三-卸载"><a class="header-anchor" href="#三-卸载">¶</a>三、卸载</h3>
736<p>  如果体验后不想使用,可以按以下方式卸载:</p>
737<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker stop immich_server immich_machine_learning immich_redis immich_postgres</span><br></pre></td></tr></table></figure>
738<p>  自行清理对应的容器和镜像,可以酌情删除 <code>/mnt/docker/immich-app</code> 文件夹。</p>
739
740      
741    </div>
742
743    <div>
744        
745            <div style="color:#555;font-size:16px;font-weight:600;margin:40px 0 20px">以上</div>
746        
747    </div>
748
749    
750
751    
752    
753    
754
755    <div>
756        
757            
758<div class="my_post_copyright">
759  
759<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/clipboard.min.js"></script>
759
760
761  <!-- JS库 sweetalert 可修改路径 -->
762  <!-- 
762<script type="text/javascript" src="http://code.jquery.com/jquery-3.2.1.min.js"></script>
762 -->
763  
763<script type="text/javascript" src="https://wise-job.oss-cn-zhangjiakou.aliyuncs.com/webjs/libs/jquery/3.2.1.min.js"></script>
763
764  
764<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/sweetalert.min.js"></script>
764
765  <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/sweetalert.min.css">
766
767  <p><span>随笔标题:</span>53k star! 高颜值的开源相册工具 —— immich</p>
768  <p><span>随笔作者:</span>刘先玉</p>
769  <p><span>发布时间:</span>2024年11月26日 - 09:42:38</p>
770  <p><span>最后更新:</span>2024年11月26日 - 09:42:38</p>
771  <p><span>原文链接:</span><a href="/article/immich-docker.html" title="53k star! 高颜值的开源相册工具 —— immich">https://liuxianyu.cn/article/immich-docker.html</a>
772    <span class="copy-path" title="点击复制文章链接"><i class="fa fa-clipboard" data-clipboard-text="https://liuxianyu.cn/article/immich-docker.html" aria-label="复制成功!"></i></span>
773  </p>
774  <!-- <p><span>许可协议:</span><i class="fa fa-creative-commons"></i> <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/" target="_blank" title="Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)">署名-非商业性使用-禁止演绎 4.0 国际</a> 转载请保留原文链接及作者。</p> -->
775</div>
776<script>
777    var clipboard = new Clipboard('.fa-clipboard');
778    clipboard.on('success', $(function(){
779      $(".fa-clipboard").click(function(){
780        swal({
781          title: "",
782          text: '复制成功',
783          html: false,
784          timer: 2000,
785          icon: "success"
786        });
787      });
788    }));
789</script>
789
790
791
792        
793    </div>
794
795    
796      
797    
798    
799
800    
801
802    
803
804    <footer class="post-footer">
805      
806        <div class="post-tags">
807          
808            <a href="/tags/Linux/" rel="tag"><i class="fa fa-envira"></i> Linux</a>
809            <!--<a href="/tags/Linux/" rel="tag"><i class="fa fa-tag"></i> Linux</a>-->
810          
811        </div>
812      
813
814
815      
816      
817      
818
819      
820        <div class="post-nav">
821          <div class="post-nav-next post-nav-item">
822            
823              <a href="/article/http-proxy.html" rel="next" title="远程服务器使用本地代理">
824                <i class="fa fa-chevron-left"></i> 远程服务器使用本地代理
825              </a>
826            
827          </div>
828
829          <span class="post-nav-divider"></span>
830
831          <div class="post-nav-prev post-nav-item">
832            
833          </div>
834        </div>
835      
836
837      
838      
839    </footer>
840  </div>
841  
842  
843  
844  </article>
845
846
847  </div>
848
849
850          </div>
851          
852
853  
854    <div class="comments" id="comments">
855    </div>
856
857  
858
859
860
861        </div>
862        
863          
864  
865  <div class="sidebar-toggle">
866    <div class="sidebar-toggle-line-wrap">
867      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
868      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
869      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
870    </div>
871  </div>
872
873  <aside id="sidebar" class="sidebar">
874    <div class="sidebar-inner">
875
876      
877
878      
879        <ul class="sidebar-nav motion-element">
880          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
881            文章目录
882          </li>
883          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
884            站点概览
885          </li>
886        </ul>
887      
888
889      <div class="site-overview-wrap sidebar-panel">
890        <div class="site-overview">
891          <div class="site-author motion-element" itemprop="author" itemscope itemtype="https://schema.org/Person">
892            
893              <a href="/">
894                <img class="site-author-image" itemprop="image" src="/images/avatar.jpg" alt="liuxy0551">
895              </a>
896            
897              <p class="site-author-name" itemprop="name">liuxy0551</p>
898              <div class="site-description motion-element" itemprop="description">Easier said than done</div>
899          </div>
900          
901            <nav class="site-state motion-element">
902              
903                <div class="site-state-item site-state-posts">
904                
905                  <a href="/archives/">
906                
907                    <span class="site-state-item-count">199</span>
908                    <span class="site-state-item-name">随笔</span>
909                  </a>
910                </div>
911              
912
913              
914                
915                
916                <div class="site-state-item site-state-categories">
917                  
918                    
919                      <a href="/categories/">
920                    
921                  
922                    
923                    
924                      
925                    
926                      
927                    
928                      
929                    
930                      
931                    
932                      
933                    
934                      
935                    
936                      
937                    
938                      
939                    
940                      
941                    
942                      
943                    
944                      
945                    
946                      
947                    
948                      
949                    
950                      
951                    
952                      
953                    
954                      
955                    
956                      
957                    
958                      
959                    
960                      
961                    
962                      
963                    
964                      
965                    
966                      
967                    
968                      
969                    
970                      
971                    
972                      
973                    
974                      
975                    
976                      
977                    
978                      
979                    
980                      
981                    
982                      
983                    
984                      
985                    
986                      
987                    
988                      
989                    
990                      
991                    
992                      
993                    
994                      
995                    
996                      
997                    
998                      
999                    
1000                      
1001                    
1002                      
1003                    
1004                      
1005                    
1006                      
1007                    
1008                      
1009                    
1010                      
1011                    
1012                      
1013                    
1014                      
1015                    
1016                      
1017                    
1018                      
1019                    
1020                      
1021                    
1022                      
1023                    
1024                      
1025                    
1026                      
1027                    
1028                      
1029                    
1030                      
1031                    
1032                      
1033                    
1034                      
1035                    
1036                      
1037                    
1038                      
1039                    
1040                      
1041                    
1042                      
1043                    
1044                      
1045                    
1046                      
1047                    
1048                      
1049                    
1050                    <span class="site-state-item-count">63</span>
1051                    <span class="site-state-item-name">分类</span>
1052                  </a>
1053                </div>
1054              
1055
1056              
1057                
1058                
1059                <div class="site-state-item site-state-tags">
1060                  
1061                    
1062                      <a href="/tags/">
1063                    
1064                  
1065                    
1066                    
1067                      
1068                    
1069                      
1070                    
1071                      
1072                    
1073                      
1074                    
1075                      
1076                    
1077                      
1078                    
1079                      
1080                    
1081                      
1082                    
1083                      
1084                    
1085                      
1086                    
1087                      
1088                    
1089                      
1090                    
1091                      
1092                    
1093                      
1094                    
1095                      
1096                    
1097                      
1098                    
1099                      
1100                    
1101                      
1102                    
1103                      
1104                    
1105                      
1106                    
1107                      
1108                    
1109                      
1110                    
1111                      
1112                    
1113                      
1114                    
1115                      
1116                    
1117                      
1118                    
1119                      
1120                    
1121                      
1122                    
1123                      
1124                    
1125                      
1126                    
1127                      
1128                    
1129                      
1130                    
1131                      
1132                    
1133                      
1134                    
1135                      
1136                    
1137                      
1138                    
1139                      
1140                    
1141                      
1142                    
1143                      
1144                    
1145                      
1146                    
1147                      
1148                    
1149                      
1150                    
1151                      
1152                    
1153                      
1154                    
1155                      
1156                    
1157                      
1158                    
1159                      
1160                    
1161                      
1162                    
1163                      
1164                    
1165                      
1166                    
1167                      
1168                    
1169                      
1170                    
1171                      
1172                    
1173                      
1174                    
1175                      
1176                    
1177                      
1178                    
1179                    <span class="site-state-item-count">56</span>
1180                    <span class="site-state-item-name">标签</span>
1181                  </a>
1182                </div>
1183              
1184
1185              
1186
1187            </nav>
1188          
1189
1190          
1191            <div class="feed-link motion-element">
1192              <a href="/atom.xml" rel="alternate">
1193                <i class="fa fa-rss"></i>
1194                RSS
1195              </a>
1196            </div>
1197          
1198
1199          
1200
1201          
1202            <div class="links-of-author motion-element">
1203              
1204                <span class="links-of-author-item">
1205                  
1206                  
1207                  
1208                    
1209                  
1210                  <a href="https://github.com/liuxy0551" title="GitHub &rarr; https://github.com/liuxy0551" rel="noopener" target="_blank"><i class="fa fa-fw fa-github"></i></a>
1211                </span>
1212              
1213                <span class="links-of-author-item">
1214                  
1215                  
1216                  
1217                    
1218                  
1219                  <a href="http://mail.qq.com/cgi-bin/qm_share?t=qm_mailme&email=MFxZRUhJAAUFAXBBQR5TX10" title="E-Mail &rarr; http://mail.qq.com/cgi-bin/qm_share?t=qm_mailme&email=MFxZRUhJAAUFAXBBQR5TX10" rel="noopener" target="_blank"><i class="fa fa-fw fa-envelope"></i></a>
1220                </span>
1221              
1222            </div>
1223          
1224
1225          
1226
1227          
1228          
1229
1230          
1231            
1232          
1233          
1234
1235        </div>
1236      </div>
1237
1238      
1239      <!--noindex-->
1240        <div class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
1241          <div class="post-toc">
1242
1243            
1244            
1245            
1246            
1247
1248            
1249              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-3"><a class="nav-link" href="#一-环境与链接"><span class="nav-text">¶一、环境与链接</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#二-安装"><span class="nav-text">¶二、安装</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#2-1-安装-docker"><span class="nav-text">
1249¶2.1 安装 docker</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-2-docker-compose-yml"><span class="nav-text">¶2.2 docker-compose.yml</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-3-大模型"><span class="nav-text">¶2.3 大模型</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-4-重启-immich"><span class="nav-text">¶2.4 重启 immich</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-5-更新-immich"><span class="nav-text">¶2.5 更新 immich</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#三-卸载"><span class="nav-text">¶三、卸载</span></a></li></ol></div>
1250            
1251
1252          </div>
1253        </div>
1254      <!--/noindex-->
1255      
1256
1257      
1258        <div class="back-to-top">
1259          <i class="fa fa-arrow-up"></i>
1260          
1261            <span id="scrollpercent"><span>0</span>%</span>
1262          
1263        </div>
1264      
1265    </div>
1266  </aside>
1267
1268  
1269
1270
1271
1272        
1273      </div>
1274    </main>
1275
1276    <footer id="footer" class="footer">
1277      <div class="footer-inner">
1278        <div class="copyright">&copy; 2017 – <span itemprop="copyrightYear">2025</span>
1279  <span class="with-love" id="animate">
1280    <i class="fa fa-heart"></i>
1281  </span>
1282  <span class="author" itemprop="copyrightHolder">刘先玉</span>
1283
1284  
1285
1286  
1287</div>
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297<!-- 页面点击小红心 -->
1298<script type="text/javascript" src="/js/src/clicklove.js"></script>
1298
1299<!-- 样式二(飘动的彩带) -->
1300<script type="text/javascript" src="/js/src/piaodai.js"></script>
1300
1301
1302<div>
1303<!-- 网站运行时间的设置 -->
1304<span id="timeDate">载入天数...</span>
1305<span id="times">载入时分秒...</span> |  Easier said than done.
1306<script>
1307    var now = new Date();
1308    function createtime() {
1309        var grt= new Date("04/05/2019 00:00:00");//此处修改你的建站时间或者网站上线时间
1310        now.setTime(now.getTime()+250);
1311        days = (now - grt ) / 1000 / 60 / 60 / 24; dnum = Math.floor(days);
1312        years = dnum / 365; ynum = Math.floor(years);
1313        dayNum = dnum - (ynum * 365);
1314        hours = (now - grt ) / 1000 / 60 / 60 - (24 * dnum); hnum = Math.floor(hours);
1315        if(String(hnum).length ==1 ){hnum = "0" + hnum;} minutes = (now - grt ) / 1000 /60 - (24 * 60 * dnum) - (60 * hnum);
1316        mnum = Math.floor(minutes); if(String(mnum).length ==1 ){mnum = "0" + mnum;}
1317        seconds = (now - grt ) / 1000 - (24 * 60 * 60 * dnum) - (60 * 60 * hnum) - (60 * mnum);
1318        document.getElementById("timeDate").innerHTML = "本站已安全运行 "+ynum+" 年 "+dayNum+" 天 ";
1319        document.getElementById("times").innerHTML = hnum + " 小时 " + mnum + " 分 ";
1320    }
1321    createtime()
1322</script>
1322
1323</div>
1324<!-- 网站底部字数统计 -->
1325<div class="theme-info">
1326  <i class="fa fa-pencil"></i>
1327  <span class="post-count">博客全站共108.3k字</span>
1328  <a style="color: #999; border-bottom: none" target="_blank" href="https://beian.miit.gov.cn/"> | 浙ICP备19046839号</a>
1329</div>
1330
1331<!-- 不蒜字统计 -->
1332<div>
1333	<i class="fa fa-user-md"></i>
1334	<span>
1335	  本站访客数:<span id="busuanzi_value_site_uv"></span>
1336	</span>&nbsp;|&nbsp;
1337
1338	<i class="fa fa-eye"></i>
1339	<span>
1340	  本站总访问量:<span id="busuanzi_value_site_pv"></span>
1341	</span>
1342</div>
1343
1344
1345<!-- 评论框美化 -->
1346<style>
1347    #comments .veditor{
1348        min-height: 10rem;
1349        background-image: url(https://images-hosting.liuxianyu.cn/comment-bg.png);
1350        background-size: contain;
1351        background-repeat: no-repeat;
1352        background-position: right;
1353        background-color: rgba(255,255,255,0);
1354        resize: none;}
1355</style>
1356
1357        
1358<div class="busuanzi-count">
1359  
1359<script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
1359
1360
1361  
1362
1363  
1364
1365  
1366</div>
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376        
1377      </div>
1378    </footer>
1379
1380    
1381
1382    
1383
1384    
1385
1386    
1387  </div>
1388
1389  
1390
1391<script>
1392  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
1393    window.Promise = null;
1394  }
1395</script>
1395
1396
1397
1398
1399
1400
1401  
1402
1403
1404
1405  
1406
1407
1408
1409
1410
1411
1412  
1413
1414
1415
1416  
1417    
1418    
1419      
1420    
1421  
1422  
1422<script color="0,0,255" opacity="0.5" zindex="-1" count="99" src="//cdn.jsdelivr.net/npm/[email protected]/dist/canvas-nest.min.js"></script>
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436  
1437  
1437<script src="//cdn.jsdelivr.net/jquery/2.1.3/jquery.min.js"></script>
1437
1438
1439  
1440  
1440<script src="//cdn.jsdelivr.net/fastclick/1.0.6/fastclick.min.js"></script>
1440
1441
1442  
1443  
1443<script src="//cdn.jsdelivr.net/jquery.lazyload/1.9.3/jquery.lazyload.min.js"></script>
1443
1444
1445  
1446  
1446<script src="//cdn.jsdelivr.net/velocity/1.2.3/velocity.min.js"></script>
1446
1447
1448  
1449  
1449<script src="//cdn.jsdelivr.net/velocity/1.2.3/velocity.ui.min.js"></script>
1449
1450
1451  
1452  
1452<script src="//cdn.jsdelivr.net/gh/fancyapps/fancybox@3/dist/jquery.fancybox.min.js"></script>
1452
1453
1454
1455  
1456
1457
1458  
1458<script src="/js/src/utils.js?v=7.0.1"></script>
1458
1459
1460  
1460<script src="/js/src/motion.js?v=7.0.1"></script>
1460
1461
1462
1463
1464  
1465  
1466
1467
1468  
1468<script src="/js/src/affix.js?v=7.0.1"></script>
1468
1469
1470  
1470<script src="/js/src/schemes/pisces.js?v=7.0.1"></script>
1470
1471
1472
1473
1474
1475  
1476  
1476<script src="/js/src/scrollspy.js?v=7.0.1"></script>
vendor: 1 bytes, line 1476
1476
1477<script src="/js/src/post-details.js?v=7.0.1"></script>
1477
1478
1479<script type="text/javascript" src="/js/src/photoswipe.min.js?v="></script>
vendor: 1 bytes, line 1479
1479
1480<script type="text/javascript" src="/js/src/photoswipe-ui-default.min.js?v="></script>
1480
1481
1482
1483
1484  
1485
1486
1487  
1487<script src="/js/src/next-boot.js?v=7.0.1"></script>
1487
1488
1489
1490  
1491  
1491<script src="/js/src/js.cookie.js?v=7.0.1"></script>
1491
1492  
1492<script src="/js/src/scroll-cookie.js?v=7.0.1"></script>
1492
1493
1494
1495  
1496
1497  
1498
1499  
1500  
1501
1502<script src="//unpkg.com/[email protected]/dist/av-min.js"></script>
1502
1503
1504
1505
1506<script src="//unpkg.com/[email protected]/dist/Valine.min.js"></script>
1506
1507
1508<script>
1509  var GUEST = ['nick', 'mail', 'link'];
1510  var guest = 'nick,mail,link';
1511  guest = guest.split(',').filter(function(item) {
1512    return GUEST.indexOf(item) > -1;
1513  });
1514  new Valine({
1515    el: '#comments',
1516    verify: true,
1517    notify: true,
1518    appId: 'EQ71ayACCXRNqBHxJTGnsUcU-gzGzoHsz',
1519    appKey: 'HUTymkI2mBJhXVc9c0ULjaWe',
1520    placeholder: '欢迎与我交流~',
1521    avatar: 'retro',
1522    meta: guest,
1523    pageSize: '10' || 10,
1524    visitor: true
1525  });
1526  //新增以下代码即可,可以移除.info下所有子节点。
1527  var infoEle = document.querySelector('#comments .info');
1528  if (infoEle && infoEle.childNodes && infoEle.childNodes.length > 0){
1529    infoEle.childNodes.forEach(function(item) {
1530      item.parentNode.removeChild(item);
1531    });
1532   }
1533</script>
1533
1534
1535
1536
1537
1538  
1539
1540
1541  
1542  
1542<script>
1543    // Popup Window;
1544    var isfetched = false;
1545    var isXml = true;
1546    // Search DB path;
1547    var search_path = "search.xml";
1548    if (search_path.length === 0) {
1549      search_path = "search.xml";
1550    } else if (/json$/i.test(search_path)) {
1551      isXml = false;
1552    }
1553    var path = "/" + search_path;
1554    // monitor main search box;
1555
1556    var onPopupClose = function (e) {
1557      $('.popup').hide();
1558      $('#local-search-input').val('');
1559      $('.search-result-list').remove();
1560      $('#no-result').remove();
1561      $(".local-search-pop-overlay").remove();
1562      $('body').css('overflow', '');
1563    }
1564
1565    function proceedsearch() {
1566      $("body")
1567        .append('<div class="search-popup-overlay local-search-pop-overlay"></div>')
1568        .css('overflow', 'hidden');
1569      $('.search-popup-overlay').click(onPopupClose);
1570      $('.popup').toggle();
1571      var $localSearchInput = $('#local-search-input');
1572      $localSearchInput.attr("autocapitalize", "none");
1573      $localSearchInput.attr("autocorrect", "off");
1574      $localSearchInput.focus();
1575    }
1576
1577    // search function;
1578    var searchFunc = function(path, search_id, content_id) {
1579      'use strict';
1580
1581      // start loading animation
1582      $("body")
1583        .append('<div class="search-popup-overlay local-search-pop-overlay">' +
1584          '<div id="search-loading-icon">' +
1585          '<i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>' +
1586          '</div>' +
1587          '</div>')
1588        .css('overflow', 'hidden');
1589      $("#search-loading-icon").css('margin', '20% auto 0 auto').css('text-align', 'center');
1590
1591      
1592        // ref: https://github.com/ForbesLindesay/unescape-html
1593        var unescapeHtml = function(html) {
1594          return String(html)
1595            .replace(/&quot;/g, '"')
1596            .replace(/&#39;/g, '\'')
1597            .replace(/&#x3A;/g, ':')
1598            // replace all the other &#x; chars
1599            .replace(/&#(\d+);/g, function (m, p) { return String.fromCharCode(p); })
1600            .replace(/&lt;/g, '<')
1601            .replace(/&gt;/g, '>')
1602            .replace(/&amp;/g, '&');
1603        };
1604      
1605
1606      $.ajax({
1607        url: path,
1608        dataType: isXml ? "xml" : "json",
1609        async: true,
1610        success: function(res) {
1611          // get the contents from search data
1612          isfetched = true;
1613          $('.popup').detach().appendTo('.header-inner');
1614          var datas = isXml ? $("entry", res).map(function() {
1615            return {
1616              title: $("title", this).text(),
1617              content: $("content",this).text(),
1618              url: $("url" , this).text()
1619            };
1620          }).get() : res;
1621          var input = document.getElementById(search_id);
1622          var resultContent = document.getElementById(content_id);
1623          var inputEventFunction = function() {
1624            var searchText = input.value.trim().toLowerCase();
1625            var keywords = searchText.split(/[\s\-]+/);
1626            if (keywords.length > 1) {
1627              keywords.push(searchText);
1628            }
1629            var resultItems = [];
1630            if (searchText.length > 0) {
1631              // perform local searching
1632              datas.forEach(function(data) {
1633                var isMatch = false;
1634                var hitCount = 0;
1635                var searchTextCount = 0;
1636                var title = data.title.trim();
1637                var titleInLowerCase = title.toLowerCase();
1638                var content = data.content.trim().replace(/<[^>]+>/g,"");
1639                
1640                content = unescapeHtml(content);
1641                
1642                var contentInLowerCase = content.toLowerCase();
1643                var articleUrl = decodeURIComponent(data.url).replace(/\/{2,}/g, '/');
1644                var indexOfTitle = [];
1645                var indexOfContent = [];
1646                // only match articles with not empty titles
1647                if(title != '') {
1648                  keywords.forEach(function(keyword) {
1649                    function getIndexByWord(word, text, caseSensitive) {
1650                      var wordLen = word.length;
1651                      if (wordLen === 0) {
1652                        return [];
1653                      }
1654                      var startPosition = 0, position = [], index = [];
1655                      if (!caseSensitive) {
1656                        text = text.toLowerCase();
1657                        word = word.toLowerCase();
1658                      }
1659                      while ((position = text.indexOf(word, startPosition)) > -1) {
1660                        index.push({position: position, word: word});
1661                        startPosition = position + wordLen;
1662                      }
1663                      return index;
1664                    }
1665
1666                    indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
1667                    indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
1668                  });
1669                  if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
1670                    isMatch = true;
1671                    hitCount = indexOfTitle.length + indexOfContent.length;
1672                  }
1673                }
1674
1675                // show search results
1676
1677                if (isMatch) {
1678                  // sort index by position of keyword
1679
1680                  [indexOfTitle, indexOfContent].forEach(function (index) {
1681                    index.sort(function (itemLeft, itemRight) {
1682                      if (itemRight.position !== itemLeft.position) {
1683                        return itemRight.position - itemLeft.position;
1684                      } else {
1685                        return itemLeft.word.length - itemRight.word.length;
1686                      }
1687                    });
1688                  });
1689
1690                  // merge hits into slices
1691
1692                  function mergeIntoSlice(text, start, end, index) {
1693                    var item = index[index.length - 1];
1694                    var position = item.position;
1695                    var word = item.word;
1696                    var hits = [];
1697                    var searchTextCountInSlice = 0;
1698                    while (position + word.length <= end && index.length != 0) {
1699                      if (word === searchText) {
1700                        searchTextCountInSlice++;
1701                      }
1702                      hits.push({position: position, length: word.length});
1703                      var wordEnd = position + word.length;
1704
1705                      // move to next position of hit
1706
1707                      index.pop();
1708                      while (index.length != 0) {
1709                        item = index[index.length - 1];
1710                        position = item.position;
1711                        word = item.word;
1712                        if (wordEnd > position) {
1713                          index.pop();
1714                        } else {
1715                          break;
1716                        }
1717                      }
1718                    }
1719                    searchTextCount += searchTextCountInSlice;
1720                    return {
1721                      hits: hits,
1722                      start: start,
1723                      end: end,
1724                      searchTextCount: searchTextCountInSlice
1725                    };
1726                  }
1727
1728                  var slicesOfTitle = [];
1729                  if (indexOfTitle.length != 0) {
1730                    slicesOfTitle.push(mergeIntoSlice(title, 0, title.length, indexOfTitle));
1731                  }
1732
1733                  var slicesOfContent = [];
1734                  while (indexOfContent.length != 0) {
1735                    var item = indexOfContent[indexOfContent.length - 1];
1736                    var position = item.position;
1737                    var word = item.word;
1738                    // cut out 100 characters
1739                    var start = position - 20;
1740                    var end = position + 80;
1741                    if(start < 0){
1742                      start = 0;
1743                    }
1744                    if (end < position + word.length) {
1745                      end = position + word.length;
1746                    }
1747                    if(end > content.length){
1748                      end = content.length;
1749                    }
1750                    slicesOfContent.push(mergeIntoSlice(content, start, end, indexOfContent));
1751                  }
1752
1753                  // sort slices in content by search text's count and hits' count
1754
1755                  slicesOfContent.sort(function (sliceLeft, sliceRight) {
1756                    if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
1757                      return sliceRight.searchTextCount - sliceLeft.searchTextCount;
1758                    } else if (sliceLeft.hits.length !== sliceRight.hits.length) {
1759                      return sliceRight.hits.length - sliceLeft.hits.length;
1760                    } else {
1761                      return sliceLeft.start - sliceRight.start;
1762                    }
1763                  });
1764
1765                  // select top N slices in content
1766
1767                  var upperBound = parseInt('1');
1768                  if (upperBound >= 0) {
1769                    slicesOfContent = slicesOfContent.slice(0, upperBound);
1770                  }
1771
1772                  // highlight title and content
1773
1774                  function highlightKeyword(text, slice) {
1775                    var result = '';
1776                    var prevEnd = slice.start;
1777                    slice.hits.forEach(function (hit) {
1778                      result += text.substring(prevEnd, hit.position);
1779                      var end = hit.position + hit.length;
1780                      result += '<b class="search-keyword">' + text.substring(hit.position, end) + '</b>';
1781                      prevEnd = end;
1782                    });
1783                    result += text.substring(prevEnd, slice.end);
1784                    return result;
1785                  }
1786
1787                  var resultItem = '';
1788
1789                  if (slicesOfTitle.length != 0) {
1790                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + highlightKeyword(title, slicesOfTitle[0]) + "</a>";
1791                  } else {
1792                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + title + "</a>";
1793                  }
1794
1795                  slicesOfContent.forEach(function (slice) {
1796                    resultItem += "<a href='" + articleUrl + "'>" +
1797                      "<p class=\"search-result\">" + highlightKeyword(content, slice) +
1798                      "...</p>" + "</a>";
1799                  });
1800
1801                  resultItem += "</li>";
1802                  resultItems.push({
1803                    item: resultItem,
1804                    searchTextCount: searchTextCount,
1805                    hitCount: hitCount,
1806                    id: resultItems.length
1807                  });
1808                }
1809              })
1810            };
1811            if (keywords.length === 1 && keywords[0] === "") {
1812              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-search fa-5x"></i></div>'
1813            } else if (resultItems.length === 0) {
1814              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-frown-o fa-5x"></i></div>'
1815            } else {
1816              resultItems.sort(function (resultLeft, resultRight) {
1817                if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
1818                  return resultRight.searchTextCount - resultLeft.searchTextCount;
1819                } else if (resultLeft.hitCount !== resultRight.hitCount) {
1820                  return resultRight.hitCount - resultLeft.hitCount;
1821                } else {
1822                  return resultRight.id - resultLeft.id;
1823                }
1824              });
1825              var searchResultList = '<ul class=\"search-result-list\">';
1826              resultItems.forEach(function (result) {
1827                searchResultList += result.item;
1828              })
1829              searchResultList += "</ul>";
1830              resultContent.innerHTML = searchResultList;
1831            }
1832          }
1833
1834          if ('auto' === 'auto') {
1835            input.addEventListener('input', inputEventFunction);
1836          } else {
1837            $('.search-icon').click(inputEventFunction);
1838            input.addEventListener('keypress', function (event) {
1839              if (event.keyCode === 13) {
1840                inputEventFunction();
1841              }
1842            });
1843          }
1844
1845          // remove loading animation
1846          $(".local-search-pop-overlay").remove();
1847          $('body').css('overflow', '');
1848
1849          proceedsearch();
1850        }
1851      });
1852    }
1853
1854    // handle and trigger popup window;
1855    $('.popup-trigger').click(function(e) {
1856      e.stopPropagation();
1857      if (isfetched === false) {
1858        searchFunc(path, 'local-search-input', 'local-search-result');
1859      } else {
1860        proceedsearch();
1861      };
1862    });
1863
1864    $('.popup-btn-close').click(onPopupClose);
1865    $('.popup').click(function(e){
1866      e.stopPropagation();
1867    });
1868    $(document).on('keyup', function (event) {
1869      var shouldDismissSearchPopup = event.which === 27 &&
1870        $('.search-popup').is(':visible');
1871      if (shouldDismissSearchPopup) {
1872        onPopupClose();
1873      }
1874    });
1875  </script>
1875
1876
1877
1878
1879
1880
1881  
1882
1883  
1884
1885  
1886  
1887
1888  
1889  
1890
1891  
1892
1893
1894  
1895<style>
1896.pdfobject-container {
1897  position: relative;
1898  overflow: auto;
1899  width: 100%;
1900}
1901</style>
1902<script>
1903if ($('body').find('div.pdf').length) {
1904  $.ajax({
1905    type: 'GET',
1906    url: '//cdn.jsdelivr.net/npm/[email protected]/pdfobject.min.js',
1907    dataType: 'script',
1908    cache: true,
1909    success: function() {
1910      $('body').find('div.pdf').each(function(i, o) {
1911        PDFObject.embed($(o).attr('target'), $(o), {
1912          pdfOpenParams: {
1913            navpanes: 0,
1914            toolbar: 0,
1915            statusbar: 0,
1916            pagemode: 'thumbs',
1917            view: 'FitH'
1918          },
1919          PDFJS_URL: '/lib/pdf/web/viewer.html',
1920          height: $(o).attr('height') || '500px'
1921        });
1922      });
1923    },
1924  });
1925}
1926</script>
1926
1927
1928
1929  
1930
1931  
1932  
1932<script>
1933    (function(){
1934      var bp = document.createElement('script');
1935      var curProtocol = window.location.protocol.split(':')[0];
1936      bp.src = (curProtocol === 'https') ? 'https://zz.bdstatic.com/linksubmit/push.js' : 'http://push.zhanzhang.baidu.com/push.js';
1937      var s = document.getElementsByTagName("script")[0];
1938      s.parentNode.insertBefore(bp, s);
1939    })();
1940  </script>
1940
1941
1942
1943  
1944  
1944<script defer src="https://umami.liuxianyu.cn/script.js" data-website-id="6b203b66-330d-4f68-b3ff-70282b97bceb"></script>
1944
1945  
1945<script>window.console.log(`%c${new Date().toLocaleString()}`, 'font-family: Cabin, Helvetica, Arial, sans-serif;text-align: left;font-size:24px;color:#B21212;');</script>
1945
1946
1947
1948  
1949
1950  
1951
1952  
1953
1954  
1955
1956  
1957
1958  
1959<style>
1960  .copy-btn {
1961    display: inline-block;
1962    padding: 6px 12px;
1963    font-size: 13px;
1964    font-weight: 700;
1965    line-height: 20px;
1966    color: #333;
1967    white-space: nowrap;
1968    vertical-align: middle;
1969    cursor: pointer;
1970    
1971      background-color: #eee;
1972      background-image: linear-gradient(#fcfcfc, #eee);
1973      border: 1px solid #d5d5d5;
1974      border-radius: 3px;
1975    
1976    user-select: none;
1977    outline: 0;
1978  }
1979
1980  .highlight-wrap .copy-btn {
1981    transition: opacity .3s ease-in-out;
1982    opacity: 0;
1983    padding: 2px 6px;
1984    position: absolute;
1985    
1986      right: 4px;
1987      top: 3px;
1988    
1989  }
1990
1991  .highlight-wrap:hover .copy-btn,
1992  .highlight-wrap .copy-btn:focus {
1993    opacity: 1;
1994  }
1995
1996  .highlight-wrap {
1997    position: relative;
1998  }
1999</style>
2000<script>
2001  $('.highlight').each(function(i, e) {
2002    var $wrap = $('<div>').addClass('highlight-wrap');
2003    $(e).after($wrap);
2004    $wrap.append($('<button>').addClass('copy-btn').append('复制').on('click', function(e) {
2005      var code = $(this).parent().find('.code').find('.line').map(function(i, e) {
2006        return $(e).text();
2007      }).toArray().join('\n');
2008      var ta = document.createElement('textarea');
2009      var yPosition = window.pageYOffset || document.documentElement.scrollTop;
2010      ta.style.top = yPosition + 'px'; // Prevent page scroll
2011      ta.style.position = 'absolute';
2012      ta.style.opacity = '0';
2013      ta.readOnly = true;
2014      ta.value = code;
2015      document.body.appendChild(ta);
2016      ta.select();
2017      ta.setSelectionRange(0, code.length);
2018      ta.readOnly = false;
2019      var result = document.execCommand('copy');
2020      
2021        if (result) $(this).text('复制成功');
2022        else $(this).text('复制失败');
2023      
2024      ta.blur(); // For iOS
2025      $(this).blur();
2026    })).on('mouseleave', function(e) {
2027      var $b = $(this).find('.copy-btn');
2028      setTimeout(function() {
2029        $b.text('复制');
2030      }, 300);
2031    }).append(e);
2032  })
2033</script>
2033
2034
2035
2036  
2037
2038</body>
2039</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.