用 R 更优雅地分享图片
楚新元 / 2021-10-17
目前我的个人主页采用 Hugo 框架、hugo-ivy 主题、blogdown 包搭建,GitLab 代码托管,Netlify 网站部署。自从有了这个网站,分享文件变得十分容易,比如,我只需要在 static/img
文件夹下放一张图片 Rlogo.png,那么我一旦推送到 GitLab,GitLab 一有动静就会触发 Netlify 自动部署。部署后这张图片的网址就一定是 https://cxy.rbind.io/img/Rlogo.png
,所以只要用 blogdown 搭建一个网站,分享文件就是一件很容易的事情。
其实和本站不相关的文件我是很不情愿放进来的。所以我又搭建了另外一个网站,这个网站因为主要是分享图片为主,所以网站长什么样子就无关紧要了,足够轻量就好,这个网站另外一个用途是测试各种好玩的功能。最后选择了益辉的 hugo-xmin 主题,这个主题很适合 blogdown 初学者学习,删繁就简,极简风格,简约而不简单,后期可以在这个主题的基础上继续美化、增加功能,只有这样你的网站才是有灵魂的,拿来主义就像是看别人放烟花,毫无乐趣可言。
以前,对于分享图片这事,我是把图片放在 static/img
目录下,然后推送到 GitLab 上就可以了。但是最近看到叶寻的博客上有一篇文章《GitHub 图床教程》,他是通过 Python 自动将图片上传到 GitHub 上,这个思路给了我启发,是否可以用 R 实现呢?
搜了下 CRAN,果然没有让我失望,git2r 包可以实现在 R 中操纵 Git 向 GitLab 推送文件的需求。代码如下:
# 加载相关 R 包
library(git2r) # Linux 下安装 git2r 前务必先安装 libssh2-1-dev
# 初始化本地仓库
local_Rproj = "/path/to/local_Rproj"
repo = init(local_Rproj)
# 定义要上传到 GitLab 上的文件
file = "xxxx.png"
path = "/path/to/"
local_file = paste0(path, file)
# 把要上传的文件复制到本地仓库指定目录下
add_file = paste0(local_Rproj, "/static/img/", file)
file.copy(local_file, add_file)
# 推送文件到 GitLab 指定仓库
add(repo, add_file)
commit(repo, paste("upload", file, sep = " "))
push(
object = repo,
name = "origin",
refspec = "refs/heads/master",
credentials = cred_ssh_key(),
set_upstream = TRUE
)
# 生成图片的 url 地址
img_url = paste0("博客地址/img/", file)
print(img_url)
运行上述代码就可以自动把图片上传到 GitLab 上,并输出图片的 URL 地址。但,在运行上述代码前,首先需要通过在 Git 里运行如下代码将密钥转化为 PEM 格式(重新创建 PEM 格式的密钥也是可以的,但是太麻烦)。
# id_rsa文件路径根据实际情况调整
ssh-keygen -p -m PEM -f ~/.ssh/id_rsa
最开始我这边没有把密钥转为 PEM 格式,在 push 那一步报错了,报错日志如下:
Error in push(object = repo, name = "origin", refspec = "refs/heads/master", :
Error in 'git2r_push': Failed to authenticate SSH session: Unable to send userauth-publickey request
这个问题我向叶寻兄弟发邮件寻求帮助,他认认真真重现整个过程,并且通过谷歌搜索到了两个可能的答案,其中一个解决方案是删除私钥底部的空行,这个方案对于我这边无效,但是这个方案指向的一个链接的另外一条评论给出的将密钥转化为 PEM 格式却有神奇疗效。在此隆重感谢叶寻兄弟的大力协助。
功能基本实现了,再做点锦上添花的工作,制作一个 Shiny app 让操作更加方便,代码如下:
#---------------------------------------------------------------------------
# 注意事项
#---------------------------------------------------------------------------
# 注意:密钥需要转为PEM格式
# 代码:ssh-keygen -p -m PEM -f ~/.ssh/id_rsa
# Linux 系统需要安装 libgit2:
# Debian: libgit2-dev
# Fedora / CentOS: libgit2-devel
# Arch Linux: libgit2
#---------------------------------------------------------------------------
# 配置环境变量
#---------------------------------------------------------------------------
# Sys.setlocale(category = "LC_ALL", locale = "zh_CN.UTF-8")
local_repo = "../xmin"
blog_url = "https://xmin.netlify.app"
#---------------------------------------------------------------------------
# 定义前端交互界面
#---------------------------------------------------------------------------
library(shiny)
ui = fluidPage(
fluidRow(
column(
width = 6,
offset = 3,
br(),
br(),
br(),
wellPanel(
fileInput(
inputId = "choose_file",
label = "选择上传文件",
buttonLabel = "浏览...",
placeholder = "没有选择文件"
),
div(
style = "display: inline-block;
width: 100%;
text-align: center;",
actionButton(
inputId = "upload_file",
label = "上传文件",
class = "btn-success",
)
),
br(),
tableOutput("file_url"),
br()
)
)
)
)
#---------------------------------------------------------------------------
# 定义后台服务逻辑
#---------------------------------------------------------------------------
server = function(input, output, session) {
# 上传文件
observeEvent(input$upload_file, {
# 定义要上传到 GitLab 上的文件
file = input$choose_file
file_name = file$name
file_path = file$datapath
# 把要上传的文件复制到本地指定目录下
img_ext = c(
"png", "PNG", "jpg", "JPG", "jpeg", "JPEG", "bmp",
"BMP", "svg", "SVG", "ico", "ICO", "tif", "TIF",
"gif", "GIF", "webp", "WEBP", "tiff", "TIFF"
)
ext = tools::file_ext(file_name)
if (ext %in% img_ext) {
dest_dir = "img"
} else {
dest_dir = "src"
}
file_add = file.path(local_repo, "static", dest_dir, file_name)
file.copy(file_path, file_add)
Sys.sleep(2)
# 推送文件到 GitLab 指定仓库
git2r::add(local_repo, file_add)
git2r::commit(local_repo, paste("upload", file_name, sep = " "))
git2r::push(
object = local_repo,
name = "origin",
refspec = "refs/heads/master",
credentials = git2r::cred_ssh_key(),
set_upstream = TRUE
)
# 生成图片的 URL 地址
file_url = file.path(blog_url, dest_dir, file_name)
# 反馈文件的 URL
output$file_url = renderTable(
print(file_url),
colnames = FALSE
)
})
}
#---------------------------------------------------------------------------
# 运行 Shiny app
#---------------------------------------------------------------------------
shinyApp(ui = ui, server = server)
#---------------------------------------------------------------------------