如何查找任何缺失的图片?

好的。这是 raw 文本:

"看起来“风王”、“马里奥”,或者简单地称为“第16号台风”将 [于周四在日本登陆](http://www.jma.go.jp/jp/typh/1416l.html):

![第16号台风](/uploads/default/35/4608d96d1b27846f.png)"

这是 cooked 文本:

"<p>看起来“风王”、“马里奥”,或者简单地称为“第<span class="hashtag">16</span>号台风”将 <a href="http://www.jma.go.jp/jp/typh/1416l.html" rel="nofollow noopener">于周四在日本登陆</a>:</p>
<p><div class="lightbox-wrapper"><a class="lightbox" href="/uploads/default/original/2X/0/01bb9fb7e29c2b65fd663cdc58705d1720f8fea7.png" title="4608d96d1b27846f.png"><img src="/uploads/default/original/2X/0/01bb9fb7e29c2b65fd663cdc58705d1720f8fea7.png" alt="第16号台风" width="602" height="500"><div class="meta">
<svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use xlink:href="#far-image"></use></svg><span class="filename">4608d96d1b27846f.png</span><span class="informations">800×664</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use xlink:href="#discourse-expand"></use></svg>
</div></a></div></p>"

当这些内容被压缩在单行时确实难以阅读,因此以下是格式化的 raw 文本:

"看起来“风王”、“马里奥”,或者简单地称为
“第16号台风”将 [于周四在日本登陆]
(http://www.jma.go.jp/jp/typh/1416l.html):

![第16号台风](/uploads/default/35/4608d96d1b27846f.png)"

以下是格式化的 cooked 文本:

"<p>
  看起来“风王”、“马里奥”,或者简单地称为
  “第<span class="hashtag">16</span>号台风”将
  <a href="http://www.jma.go.jp/jp/typh/1416l.html" rel="nofollow noopener">
    于周四在日本登陆
  </a>:
</p>

<p>
  <div class="lightbox-wrapper">
    <a class="lightbox" href="/uploads/default/original/2X/0/01bb9fb7e29c2b65fd663cdc58705d1720f8fea7.png" title="4608d96d1b27846f.png">
      <img src="/uploads/default/original/2X/0/01bb9fb7e29c2b65fd663cdc58705d1720f8fea7.png" alt="第16号台风" width="602" height="500">
      <div class="meta">

        <svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true">
          <use xlink:href="#far-image"></use>
        </svg>
        <span class="filename">4608d96d1b27846f.png</span>
        <span class="informations">800×664</span>
        <svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true">
          <use xlink:href="#discourse-expand"></use>
        </svg>

      </div>
    </a>
  </div>
</p>"

顺便提一下,我的网站上有不少(超过一百条)类似这样的帖子:

[1] pry(main)> Post.where("raw ~* :regex AND cooked !~* :regex", regex: '/uploads/default/[0-9]+/').count
=> 135

原始内容和已渲染内容中的 URL 格式不应不同。能否请您重新烘焙上述帖子?您可以通过帖子菜单中的 重建 HTML 选项,或运行 post.rebake! 命令来完成。此帖子中是否存在任何与“上传”相关的自定义字段?您可以通过 post.custom_fields 命令查看所有自定义字段。

以下是该特定帖子(在运行 Rebuild HTML 命令之前)的所有其他自定义字段:

  id: 43,
  user_id: 1,
  topic_id: 36,
  post_number: 3,
  created_at: Mon, 22 Sep 2014 05:05:16 UTC +00:00,
  updated_at: Mon, 22 Sep 2014 05:11:22 UTC +00:00,
  reply_to_post_number: nil,
  reply_count: 0,
  quote_count: 0,
  deleted_at: nil,
  off_topic_count: 0,
  like_count: 0,
  incoming_link_count: 0,
  bookmark_count: 0,
  avg_time: 58,
  score: 1.2,
  reads: 6,
  post_type: 1,
  sort_order: 3,
  last_editor_id: -1,
  hidden: false,
  hidden_reason_id: nil,
  notify_moderators_count: 0,
  spam_count: 0,
  illegal_count: 0,
  inappropriate_count: 0,
  last_version_at: Mon, 22 Sep 2014 05:11:22 UTC +00:00,
  user_deleted: false,
  reply_to_user_id: nil,
  percent_rank: 0.585365853658537,
  notify_user_count: 0,
  like_score: 0,
  deleted_by_id: nil,
  edit_reason: "downloaded local copies of images",
  word_count: 34,
  version: 2,
  cook_method: 1,
  wiki: false,
  baked_at: Sun, 14 Apr 2019 09:28:00 UTC +00:00,
  baked_version: 2,
  hidden_at: nil,
  self_edits: 2,
  reply_quoted: false,
  via_email: false,
  raw_email: nil,
  public_version: 2,
  action_code: nil,
  image_url: "/uploads/default/original/2X/0/01bb9fb7e29c2b65fd663cdc58705d1720f8fea7.png",
  locked_by_id: nil

我没有看到 uploads 字段,但也许 image_url 就是你要找的内容?在运行 Rebuild HTML 命令之前,它的值为:

/uploads/default/original/2X/0/01bb9fb7e29c2b65fd663cdc58705d1720f8fea7.png

运行 Rebuild HTML 命令似乎已将 image_url 字段的值更改为:

https://{{SITE FQDN}}/uploads/default/35/4608d96d1b27846f.png

烹饪文本中的所有 URL 似乎也已更新:

"<p>
  看起来“风王”、“马里奥”或简单地称为
  “台风 <span class=\"hashtag\">#16</span>”将
  <a href=\"http://www.jma.go.jp/jp/typh/1416l.html\" rel=\"nofollow noopener\">
    于周四在日本登陆
  </a>:
 </p>\n
 <p>
   <div class=\"lightbox-wrapper\">
     <a class=\"lightbox\" href=\"https://{{SITE FQDN}}/uploads/default/35/4608d96d1b27846f.png\">
       <img src=\"https://{{SITE FQDN}}/uploads/default/35/4608d96d1b27846f.png\" alt=\"Typhoon 16\" width=\"602\" height=\"500\">
       <div class=\"meta\">\n
         <svg class=\"fa d-icon d-icon-far-image svg-icon\" aria-hidden=\"true\">
           <use xlink:href=\"#far-image\"></use>
         </svg>
         <span class=\"filename\">4608d96d1b27846f.png</span>
         <span class=\"informations\">800×664</span>
         <svg class=\"fa d-icon d-icon-discourse-expand svg-icon\" aria-hidden=\"true\">
           <use xlink:href=\"#discourse-expand\"></use>
         </svg>\n
       </div>
     </a>
   </div>
 </p>"

4608d96d1b27846f.png01bb9fb7e29c2b65fd663cdc58705d1720f8fea7.png 之间的关系是什么?它们的尺寸相同,乍一看看起来一样,但它们显然是不同的文件:

$ diff /var/discourse/shared/standalone/uploads/default/35/4608d96d1b27846f.png /var/discourse/shared/standalone/uploads/default/original/2X/0/01bb9fb7e29c2b65fd663cdc58705d1720f8fea7.png
Binary files /var/discourse/shared/standalone/uploads/default/35/4608d96d1b27846f.png and /var/discourse/shared/standalone/uploads/default/original/2X/0/01bb9fb7e29c2b65fd663cdc58705d1720f8fea7.png differ

$ ls -l /var/discourse/shared/standalone/uploads/default/35/4608d96d1b27846f.png
-rw-r--r-- 1 chris www-data 150319 Jan 19 01:14 /var/discourse/shared/standalone/uploads/default/35/4608d96d1b27846f.png

$ ls -l /var/discourse/shared/standalone/uploads/default/original/2X/0/01bb9fb7e29c2b65fd663cdc58705d1720f8fea7.png
-rw-r--r-- 1 chris chris 95005 Jul  3 15:25 /var/discourse/shared/standalone/uploads/default/original/2X/0/01bb9fb7e29c2b65fd663cdc58705d1720f8fea7.png

当然,百万美元的问题依然存在:我该如何将 /uploads/default/35/4608d96d1b27846f.png 迁移到新的上传方案?

看起来您的上传文件并未正确迁移到新的方案。设置 SiteSetting.migrate_to_new_scheme = true 本身应能解决此问题。我不确定为何在您的情况下未生效。请检查尚未迁移到新方案的上传文件数量。运行以下命令以获取结果。

Upload.by_users.where("url NOT LIKE '%/original/_X/%' AND url LIKE '%/uploads/default%'").count
SiteSetting.migrate_to_new_scheme = true
Jobs::MigrateUploadScheme.new.execute(nil)
Upload.by_users.where("url NOT LIKE '%/original/_X/%' AND url LIKE '%/uploads/default%'").count

不,这些不是自定义字段。您应通过命令 post.custom_fields 获取自定义字段的值。

哦,抱歉!我完全误解了你所说的“自定义字段”的意思。

我可能错了,但看起来那篇帖子并没有任何自定义字段:

[1] pry(main)> Post.find_by(:id => 43).custom_fields
=> {}

嗯,这很有趣……

[2] pry(main)> Upload.by_users.where("url NOT LIKE '%/original/_X/%' AND url LIKE '%/uploads/default%'").count
=> 0
[3] pry(main)> Post.find_by(:id => 43).image_url
=> "https://{{SITE FQDN}}/uploads/default/35/4608d96d1b27846f.png"

看起来你提供的查询没有匹配到任何结果。这是预期的行为吗?

另外:

你知道这个问题的答案可能是什么吗?

看起来您的上传内容已经迁移到了新方案,但帖子未能正确映射到新方案的 URL。这些帖子现在处于混乱状态。能否通过私信提供您网站的凭证?这样我可以在空闲时进一步调查。

啊,这正是我担心的。不幸的是,这是一个私有安装,我不确定是否有权将服务器(root)权限授予外部人员。除此之外,我想不出还有什么其他方法可以排查这个问题了?

我从四月底左右就开始遇到图片缺失的问题,但直到今晚才开始调查。

rake posts:missing_uploads
缺失 26766 个帖子上传文件。

缺失 22693 个上传文件。
其中 22683 个属于旧方案上传。
受影响的帖子数量为 9352 个(共 535188 个帖子)。

我们使用的是稳定版……考虑到本线程中最近的帖子,我不确定接下来该怎么做。

编辑:我选择了一个特定的 GIF 文件,发现它在生产服务器的上传目录中不存在,但在备份服务器的 tombstone 目录中确实存在。我通过 scp 将该文件从备份服务器(discourse/shared/standalone/uploads/tombstone/default/39/ee8670816301d4c4.gif)复制到生产服务器对应的 tombstone 目录,然后重新运行了上述 rake 任务作为测试。

现在该帖子中的图片已正常显示,总体数字也降至:

缺失 26750 个帖子上传文件。

缺失 22692 个上传文件。
其中 22682 个属于旧方案上传。
受影响的帖子数量为 9336 个(共 535190 个帖子)。

看起来生产服务器上的 tombstone 目录大小为 138MB,而备份服务器上为 9.5GB。因此,我将对该目录执行一次 rsync 同步,并再次运行 rake 任务,希望这样可以进一步降低报告的缺失数量。

@kansaichris 看起来你只有 3 篇帖子缺少上传内容。在这种情况下,你应该手动编辑这些帖子的原始内容,填入正确的上传 URL。

@skl 你有大量旧方案的上传文件。在从备份服务器复制墓碑(tombstone)上传文件后,请运行以下命令将其迁移到新方案。

rake posts:missing_uploads    # 确保 `rsync` 已复制文件
rake uploads:recover          # 如果 rsync 后仍有缺失的上传
SiteSetting.migrate_to_new_scheme = true
Jobs::MigrateUploadScheme.new.execute(nil)
Upload.by_users.where("url NOT LIKE '%/original/_X/%' AND url LIKE '%/uploads/default%'").count
# 确保计数为 0
rake posts:missing_uploads    # 再次检查状态

感谢您的帮助,@vinothkannans。我按照您的指示操作(执行耗时约 12 小时),数字有所下降:

缺失 22614 个帖子上传。
缺失 19830 个上传。
19830 个缺失上传中有 19821 个属于旧方案上传。
535224 个帖子中有 7339 个受到影响。

由于仍有缺失的上传,我查看了 tombstone 文件夹之外,发现生产服务器上 uploads/default 目录显示有 22,885 个空目录(备份服务器上为 10 个空目录)。此外,备份服务器上的文件大小还多出 10GB 以上,因此我打算现在从备份服务器将 uploads/default 目录 rsync 到生产服务器,然后再次执行您的指示。

编辑:rake posts:missing_uploads 似乎是一个受 CPU 限制的单线程任务,已运行超过 30 小时,因此我暂时将服务器扩容至专用 CPU 实例。图片目前似乎已恢复,尽管是以旧方案形式呈现,因此推测最初导致删除的可能是某次 Discourse 更新。

嗯……如果真的只有 3 个帖子存在上传缺失,为什么似乎有 135 个帖子的原始文本使用了旧的上传方案,而其渲染后的文本却使用了新的上传方案?

[1] pry(main)> Post.where("raw ~* :regex AND cooked !~* :regex", regex: '/uploads/default/[0-9]+/').count
=> 135

这是因为 rawcooked 列中的上传 URL 方案不匹配。posts:missing_uploads rake 任务只会检查 cooked 列中的上传记录。你必须修复那些不匹配的上传 URL。如果不查看数据库,我无法提供帮助。

:crossed_fingers:

啊,明白了,我之前没意识到 posts:missing_uploads 任务仅检查 cooked 列——这确实能解释这种差异。:+1:

是否可以这样理解:通过设置 SiteSetting.migrate_to_new_schemetrue 启动的迁移过程,同样也只检查 cooked 列的值?

新方案迁移将 替换 rawcooked 中的 URL。但在您的情况下并未发生。[quote=“kansaichris, post:54, topic:115672”]可以这样说吗:迁移过程是通过将 SiteSetting.migrate_to_new_scheme 设置为 true 来启动的?[/quote]我认为是的。您也可以检查受影响帖子的 last_updated_at 列。

任务因错误而中止,并且每次尝试都显示相同的错误:

[2019-07-26T09:18:56.829375 #572]  WARN -- : 格式错误的 IFD:nil:NilClass 未定义方法 `map'
....rake 中止!
ArgumentError: 提供了负长度 -2
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/exifr-1.3.6/lib/exifr/jpeg.rb:89:in `readframe'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/exifr-1.3.6/lib/exifr/jpeg.rb:116:in `examine'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/exifr-1.3.6/lib/exifr/jpeg.rb:34:in `block in initialize'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/exifr-1.3.6/lib/exifr/jpeg.rb:34:in `open'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/exifr-1.3.6/lib/exifr/jpeg.rb:34:in `initialize'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/discourse_image_optim-0.26.2/lib/image_optim/worker/jhead.rb:40:in `new'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/discourse_image_optim-0.26.2/lib/image_optim/worker/jhead.rb:40:in `oriented?'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/discourse_image_optim-0.26.2/lib/image_optim/worker/jhead.rb:27:in `optimize'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/discourse_image_optim-0.26.2/lib/image_optim.rb:122:in `block (5 levels) in optimize_image'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/discourse_image_optim-0.26.2/lib/image_optim/handler.rb:41:in `process'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/discourse_image_optim-0.26.2/lib/image_optim.rb:122:in `block (4 levels) in optimize_image'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/discourse_image_optim-0.26.2/lib/image_optim.rb:120:in `each'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/discourse_image_optim-0.26.2/lib/image_optim.rb:120:in `block (3 levels) in optimize_image'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/discourse_image_optim-0.26.2/lib/image_optim.rb:247:in `block in with_timeout'
任务:TOP => posts:missing_uploads

你使用的是最新版本还是某个旧版本?

最新稳定版“2.3.2 +4”

您很可能需要处于最新的测试版。

如果您是自己托管,那么使用稳定版并没有太多理由。实际上,它更难维护。

客户明确要求继续使用稳定版。我在接手该项目时,他们对此态度坚决。