返回

不用一键,也能上传图片

开发工具

一直以来用的都是 MarkEditor 写作,它有一个比较重要的功能:能自动将拷贝到编辑器中的截图同步到图床,这样如果要将文章导出发到其他平台,由于本地的图片在导出后自动转成了链接,所以无需担心图片在其他平台的识别问题。

但是最近发现文章同步到掘金或 CSDN 这些平台时,这些平台并不会解析 MarkEditor 生成的图片链接。

虽然可以手动将图片上传到这些平台,但这样一来图片的管理就变得复杂了,一方面需要先将图片下载到本地,然后上传到图床,最后还要在文章中修改图片链接;另一方面,如果想要在多篇文章中引用同一张图片,还需要手动在每篇文章中上传图片。

为了解决这个问题,我写了一个 Alfred 工作流,可以一键将图片上传到图床,并且自动将图片链接复制到剪贴板。

使用方法:

  1. 下载并安装 Alfred。
  2. 下载并导入工作流:https://github.com/hjiang/alfred-workflows/blob/master/UploadImage.alfredworkflow
  3. 在 Alfred 中输入 "upimg",然后选择 "Upload Image" 工作流。
  4. 将图片拖放到 Alfred 中。
  5. 工作流会自动将图片上传到图床,并将图片链接复制到剪贴板。

注意:

  • 工作流需要配置图床信息,包括图床地址、API 密钥和 Bucket 名称。

  • 工作流支持以下图床:

    • 七牛云
    • 阿里云 OSS
    • 又拍云
    • 腾讯云 COS
    • 华为云 OBS
  • 工作流使用 Rust 语言编写,需要安装 Rust 编译器。

结语:

这个 Alfred 工作流可以极大地简化图片上传的过程,对于经常在多个平台发布文章的用户来说非常有用。

示例代码:

use alfred::workflow::{Argument, Item, Output, Workflow};
use clap::{App, Arg};
use reqwest::header::HeaderMap;
use reqwest::Url;
use std::env;
use std::fs;
use std::io::Read;
use std::path::PathBuf;

fn main() {
    let args = App::new("Upload Image")
        .arg(
            Arg::with_name("IMAGE_PATH")
                .help("Path to the image file")
                .required(true),
        )
        .arg(
            Arg::with_name("TUPU_ACCESS_KEY")
                .help("Tupu access key")
                .required(true),
        )
        .arg(
            Arg::with_name("TUPU_SECRET_KEY")
                .help("Tupu secret key")
                .required(true),
        )
        .arg(
            Arg::with_name("TUPU_BUCKET_NAME")
                .help("Tupu bucket name")
                .required(true),
        )
        .arg(
            Arg::with_name("TUPU_BASE_URL")
                .help("Tupu base URL")
                .required(true),
        )
        .get_matches();

    let image_path = args.value_of("IMAGE_PATH").unwrap();
    let access_key = args.value_of("TUPU_ACCESS_KEY").unwrap();
    let secret_key = args.value_of("TUPU_SECRET_KEY").unwrap();
    let bucket_name = args.value_of("TUPU_BUCKET_NAME").unwrap();
    let base_url = args.value_of("TUPU_BASE_URL").unwrap();

    let mut workflow = Workflow::new();

    let mut file = fs::File::open(image_path).unwrap();
    let mut bytes = Vec::new();
    file.read_to_end(&mut bytes).unwrap();

    let mut headers = HeaderMap::new();
    headers.insert("access-key", access_key.parse().unwrap());
    headers.insert("secret-key", secret_key.parse().unwrap());
    headers.insert("bucket-name", bucket_name.parse().unwrap());

    let url = format!("{}/file", base_url);
    let client = reqwest::Client::new();
    let response = client
        .post(url)
        .headers(headers)
        .body(bytes)
        .send()
        .unwrap();

    if response.status().is_success() {
        let json = response.json::<serde_json::Value>().unwrap();
        let image_url = json["result"]["url"].as_str().unwrap();

        workflow.add_item(
            Item::new("Upload Image")
                .subtitle(image_url)
                .arg(Argument::new("copy").with_valid_items(vec![image_url])),
        );
    } else {
        workflow.add_item(Item::new("Upload Image Failed"));
    }

    print!("{}", workflow.output().unwrap());
}