file-uploader-cli 关于上传至京东云中文件夹问题的源码修改

file-uploader-cli源码修改

注:可以直接跳到最下面看需要修改的部分

最近由于Gitee的开源审查,导致其无法再作为图床使用。在对比了多家OSS服务后,决定使用京东云作为新图床,并使用file-uploader-cli来进行图片和文件的上传,其中Typora只使用其进行博客图片的上传。但查看了file-uploader-cli对于京东云的相关配置,并且进行多次测试后,发现它只能将文件上传到图床的根目录中。而如果在bucket参数中加入根目录中的文件夹,返回的值就会出错。这很不利于Typora在将图片自动转为网络地址后进行展示。查询解决办法无果后,只能将其源码进行修改。

Typora图片自动上传至博客图片文件夹

分析配置文件(具体配置可查看原作者的README

file-uploader-cli给京东云的配置参数如下

{
  "bucket": "*",
  "region": "*",
  "accessKeyId": "*",
  "secretAccessKey": "*"
}

其中bucket是用来配置图床地址的,比如我的是rezc404-img-hostregion是服务所在地,官网中的Bucket域名也会写到,如cn-north-1;后两个是京东云提供的访问京东云API的密钥,可以在官网自行添加和删除。

可以在任意位置编写完此JSON文件后使用fuc -t 配置类型 -c 文件名.json,比如fuc -t jdcloud -c jdcloud.json,此时打开file-uploader-cli安装目录中的config.json文件就会发现程序添加了一段配置

{
    "default": "jdcloud",
    "jdcloud": {
        "bucket": "rezc404-img-host",
        "region": "cn-east-2",
        "accessKeyId": "*",
        "secretAccessKey": "*"
    }
}

其中jdcloud对象中的参数就是之前配置的参数。此时随便找张截图,使用fuc 文件绝对路径,就可以将文件上传至图床,并返回网络地址

image-20220826231616046

image-20220826231733206

现在我想将上传至图床中的对象进行分类,图片放一起,文件放一起,可又因为上传后京东云OSS服务不支持文件的移动,这就要求我们必须在上传时选好路径。

可以在配置bucket时加上文件夹路径,比如 rezc404-img-host/blog-img,这时虽然可以上传,但是返回的网络地址rezc404-img-host/blog-img.s3.cn-east-2.jdcloud-oss.com/test.png却是错误的,这导致直接在Typora中设定自定义命令fuc后自动更改的网络地址也是错误的。

而在网站中查看外链则是rezc404-img-host.s3.cn-east-2.jdcloud-oss.com/blog-img/test.png

可以看到文件夹路径在上传时是可以放在bucket中,但最后网络地址的结构是Bucket域名/文件夹名/文件名,所以并不能简单的在bucket参数中加入文件夹。

那该如何将文件上传至指定的文件夹中,且返回地址也正确呢?

答:参考file-uploader-cli关于github的配置,添加这个path参数

分析源码

查看关于jdcloud的源代码jdcloud.js

#!/usr/bin/env node
const sharp = require('sharp')
const fs = require('fs')
const stream = require('stream')
const AWS = require('aws-sdk')
const path = require('path')
const FileType = require('file-type')
const mimeTypes = require('mime-types')
const { supportImages } = require('./util')

async function upload (config, fileList){
    const { accessKeyId, secretAccessKey, region, bucket, webp, quality } = config   //1.获取config.json中关于jdcloud的参数
    const s3 = new AWS.S3()
    s3.endpoint = `https://s3.${region}.jdcloud-oss.com`							 
    s3.config.update({
      accessKeyId,
      secretAccessKey,
      s3ForcePathStyle: true,
      region,
      signatureVersion: "v4"
    })
    async function put (filePath) {
        const mime = (await FileType.fromFile(filePath) || {}).mime || mimeTypes.lookup(filePath)
        try {
            let name = null, data = null;
            if(webp && supportImages.indexOf(path.extname(filePath)) > -1){
                name = `${path.basename(filePath,path.extname(filePath))}.webp`
                data = await sharp(filePath)
                    .webp({ quality: quality || 75 })
                    .toBuffer();
                // 创建一个bufferstream
                filePath = path.basename(filePath, path.extname(filePath)) + '.webp'
                readableStream = new stream.PassThrough()
                //将Buffer写入
                readableStream.end(data)
            }else{
                name = path.basename(filePath)
                data = fs.createReadStream(filePath)
            }
            var params = {
                Body: data, 
                Bucket: bucket,	   //2.上传的地址为config中叫bucket的参数,在后面加入"/文件夹名"就行
                ContentType: mime,
                Key: name,
            }
            s3.putObject(params, function(err, data) {
                if(err) console.log(err)
                !err && console.log(`https://${bucket}.s3.${region}.jdcloud-oss.com/${name}`)  //3.返回的网络地址格式,其中bucket是直接插入其中,所以如果将文件夹名也加入bucket中,那么返回的结果也将是错的
            });
        } catch (e) {
            console.log(e);
        }
    }
    for(let i = 0; i < fileList.length; i++){        
        await put(fileList[i])
    }
}

module.exports = upload

修改源码

其中12行,42行和48行是重点:

  1. 12行中的变量对应的正是配置文件中的参数,所以只需要在其中添加一个路径参数cloudPath即可

    const { accessKeyId, secretAccessKey, cloudPath, region, bucket, webp, quality } = config 
    
  2. 42行中Bucket为上传地址,value值中的bucket如果带上文件夹,就会传入图床中的文件夹,所以这里只需将value改为

    bucket + cloudPath,
    

    即可

  3. 48行打印出来的是图片上传后的网络地址,不难看出如果bucket中带文件夹,返回的地址就不会正确,所以只需要把cloudPath参数加在文件名前即可

    !err && console.log(`https://${bucket}.s3.${region}.jdcloud-oss.com${cloudPath}/${name}`)
    
  4. 最后只需要在配置文件中加入cloudPath 即可(注:如果想放在根目录就只需要"",如果要放入文件夹前面必须要加/)

    {
        "default": "jdcloud",
        "jdcloud": {
            "bucket": "rezc404-img-host",
            "region": "cn-east-2",
            "cloudPath": "/blog-img",
            "accessKeyId": "*",
            "secretAccessKey": "*"
        }
    }
    

image-20220828232500309

这时返回的地址就是正确的地址,在Typora中自动转换的路径也是正确的

image-20220828233730463

博客中上传的图片会传入blog-img文件夹中。如果我想向别的文件夹上传文件,比如博客的缩略图文件夹,那该怎么办呢?

答:多创建几个配置配置对象即可

不同文件上传至指定文件夹

分析源码

因为我目前只使用京东云作为图床,所以只需要添加另一段配置,然后使用同一个的上传代码,也就是jdcloud.js就可以了。

配置文件

{
    "default": "jdcloud",
    "jdcloud": {
        "bucket": "rezc404-img-host",
        "region": "cn-east-2",
        "cloudPath": "/blog-img",
        "accessKeyId": "*",
        "secretAccessKey": "*"
    }
}

修改成如下

{
    "default": "jdcloud-img",
    "jdcloud-img": {
        "bucket": "rezc404-img-host",
        "region": "cn-east-2",
        "cloudPath": "/blog-img",
        "accessKeyId": "*",
        "secretAccessKey": "*"
    },
    "jdcloud-thn": {
        "bucket": "rezc404-img-host",
        "region": "cn-east-2",
        "cloudPath": "/blog-thumbnails",
        "accessKeyId": "*",
        "secretAccessKey": "*"
    }
}

但此时因为index.js

#!/usr/bin/env node
const { program } = require('commander')
const helpOptions = require('./lib/core/help')
const generateConfig = require('./lib/core/generateConfig')
const pkg = require('./package.json')
const configOperate = require('./lib/core/configOperate')
const { readConfig, absolutePath, supportList } = require('./lib/util')

helpOptions()

program
    .version(pkg.version)
    .parse(process.argv);

configOperate()

const config = generateConfig()
const options = program.opts()
const type = options['type'] || readConfig()['default']
if(supportList.indexOf(type) > -1){  //const supportList = ['github', 'ali-oss', 'qiniu', 'ftp', 'cos', 'jdcloud']
    if(options.length === 0 && program.args.length === 0){
        console.log('please specify the file to upload')
        return
    }
    const fileList = absolutePath(program.args)
    if(fileList.length > 0 && (!config || Object.keys(config).length === 0)){
        console.log("can't find configuration")
        return
    }
    fileList.length > 0 && require(`./lib/${type}`)(config,fileList)
}

supportList里没有jdcloud-imgjdcloud-thn,所以程序找不到上传的入口。

修改源码

解决办法有两种:

  1. supportList中添加这两个type,并复制**\file-uploader-cli\lib**中的jdcloud.js,改名成配置名jdcloud-img.js jdcloud-thn.js
  2. 修改源码,将传入的type的”-“后的部分去掉

我选择了第二种,在19行后添加

const minusIndex = type.indexOf('-')
const typeStr = minusIndex > -1 ? type.substring(0, minusIndex) : type

并把下面的type都换成typeStr

本来认为已经可以使用了,没想到第17行的generateConfig()函数报错,进去查看

const { program } = require('commander')
const { updateFile, readOptionConfig, readConfig } = require('../util')

const generateConfig = () => {
    let config = null
    const options = program.opts()
    // 如果指定配置  使用当前指定
    if(options.config){
        if(!options.type){
            throw new Error('configuration needs to specify the type')
        }else{
            config = readOptionConfig(options.config)//具体函数在下面
            updateFile(options.type, config)
        }
    }else if(options.type){
        config = readConfig()[options.type]
    }else{
        const setting = readConfig()
        config = setting[setting.default]
    }
    return config
}

module.exports = generateConfig
// 读取选项指定配置
const readOptionConfig = (file) => {
    createConfigFile()
    console.log(path.resolve(process.cwd(), file))
    const config = JSON.parse(readFileSync(path.resolve(process.cwd(), file), 'utf8'));
    return config
}

发现如果上传使用指定配置,程序会判断此配置中是否说明其种类,只有含有种类的配置程序才会在config.json中找到它。但改写之后配置一没有种类,二在运行readOptionConfig()函数中process.cwd()返回的路径也是错误的。我认为反正我目前也只使用京东云这一种配置,就索性把判断种类这一步去掉,并使用最后一个else中的逻辑

 if(options.config){
     	const setting = readConfig()
        config = setting[options.config]
 }

这是返回的config就是所选的配置,而且图片也成功上传

image-20220904231026321

image-20220904231052929

至此,对file-uploader-cli的源码修改已经全部结束。

修改部分汇总

  1. \file-uploader-cli\lib\jdcloud.js

    #!/usr/bin/env node
    const sharp = require('sharp')
    const fs = require('fs')
    const stream = require('stream')
    const AWS = require('aws-sdk')
    const path = require('path')
    const FileType = require('file-type')
    const mimeTypes = require('mime-types')
    const { supportImages } = require('./util')
    
    async function upload(config, fileList) {
        const { accessKeyId, secretAccessKey, cloudPath, region, bucket, webp, quality } = config
        const s3 = new AWS.S3()
        s3.endpoint = `https://s3.${region}.jdcloud-oss.com`
        s3.config.update({
            accessKeyId,
            secretAccessKey,
            s3ForcePathStyle: true,
            region,
            signatureVersion: "v4"
        })
        async function put(filePath) {
            const mime = (await FileType.fromFile(filePath) || {}).mime || mimeTypes.lookup(filePath)
            try {
                let name = null, data = null;
                if (webp && supportImages.indexOf(path.extname(filePath)) > -1) {
                    name = `${path.basename(filePath, path.extname(filePath))}.webp`
                    data = await sharp(filePath)
                        .webp({ quality: quality || 75 })
                        .toBuffer();
                    // 创建一个bufferstream
                    filePath = path.basename(filePath, path.extname(filePath)) + '.webp'
                    readableStream = new stream.PassThrough()
                    //将Buffer写入
                    readableStream.end(data)
                } else {
                    name = path.basename(filePath)
                    data = fs.createReadStream(filePath)
                }
                var params = {
                    Body: data,
                    Bucket: bucket + cloudPath,
                    ContentType: mime,
                    Key: name,
                }
                s3.putObject(params, function (err, data) {
                    if (err) console.log(err)
                    !err && console.log(`https://${bucket}.s3.${region}.jdcloud-oss.com${cloudPath}/${name}`)
                });
            } catch (e) {
                console.log(e);
            }
        }
        for (let i = 0; i < fileList.length; i++) {
            await put(fileList[i])
        }
    }
    
    module.exports = upload
    

    修改第12行,42行和48行

  2. \config.json

    {
        "default": "jdcloud",
        "jdcloud": {
            "bucket": "rezc404-img-host",
            "region": "cn-east-2",
            "cloudPath": "/blog-img",
            "accessKeyId": "*",
            "secretAccessKey": "*"
        }
    }
    

    添加第6行

  3. \file-uploader-cli\index.js

    #!/usr/bin/env node
    const { program } = require('commander')
    const helpOptions = require('./lib/core/help')
    const generateConfig = require('./lib/core/generateConfig')
    const pkg = require('./package.json')
    const configOperate = require('./lib/core/configOperate')
    const { readConfig, absolutePath, supportList } = require('./lib/util')
    
    helpOptions()
    
    program
        .version(pkg.version)
        .parse(process.argv);
    
    configOperate()
    
    const config = generateConfig()
    const options = program.opts()
    const type = options['type'] || readConfig()['default']
    const minusIndex = type.indexOf('-')
    const typeStr = minusIndex > -1 ? type.substring(0, minusIndex) : type
    if (supportList.indexOf(typeStr) > -1) {
        if (options.length === 0 && program.args.length === 0) {
            console.log('please specify the file to upload')
            return
        }
        const fileList = absolutePath(program.args)
        if (fileList.length > 0 && (!config || Object.keys(config).length === 0)) {
            console.log("can't find configuration")
            return
        }
        fileList.length > 0 && require(`./lib/${typeStr}`)(config, fileList)
    }
    

    添加第19,20行

  4. \file-uploader-cli\lib\core\generateConfig.js

    const { program } = require('commander')
    const { updateFile, readOptionConfig, readConfig } = require('../util')
    
    const generateConfig = () => {
        let config = null
        const options = program.opts()
        // 如果指定配置  使用当前指定
        if(options.config){
            const setting = readConfig()
            config = setting[options.config]
        }else if(options.type){
            config = readConfig()[options.type]
        }else{
            const setting = readConfig()
            config = setting[setting.default]
        }
        return config
    }
    
    module.exports = generateConfig
    

    修改第8行后if里的代码

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值