利用阿里云 OSS 服务为 sublime 提供自动上传图片的图片的插件

OSS 的相关操作

云对象存储服务(Object Storage Service,简称 OSS),能够提供的海量、安全、低成本、高可靠的云存储服务。通俗的讲就是一个『网络上的大硬盘』,你可以通过自己写代码进行读写、访问控制等操作。

对象存储的概念

视频播放,这里会介绍图床只是一种OSS的应用。

阿里官方的链接

1、 申请相关的 OSS 账号

2、 获得申请后的 OSS 账号的 、购买 OSS 服务、创建存储空间。 在 OSS 的管理控制台,“概览”中可以看到

阿里云 OSS 控制台

如果不存在 AccessKey 则按照官方的教程进行新建子账号,并且为改子账号提供 OSS 服务的读写权限。

3、 验证 OSS 服务,能否通过 sdk 进行相关的操作。主要的编程开发参考

4、 完成将本地图片通过代码自动上传到 OSS

本插件程序采用 Python 语言开发,首先是因为比较方便,其次 sublime 插件也是基于 Python 进行的二次开发。官方 API

  • 通过程序获得指定 bucket 名称的 bucket 对象
# -*- coding: utf-8 -*-
import oss2

# 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建RAM账号。
auth = oss2.Auth('<yourAccessKeyId>', '<yourAccessKeySecret>')
# Endpoint以杭州为例,其它Region请按实际情况填写。
bucket = oss2.Bucket(auth, 'http://oss-cn-hangzhou.aliyuncs.com', '<yourBucketName>')

# 设置存储空间为私有读写权限。
bucket.create_bucket(oss2.models.BUCKET_ACL_PRIVATE)
  • 往指定的 Bucket 中上传图片(文件)
# -*- coding: utf-8 -*-
import oss2

# 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建RAM账号。
auth = oss2.Auth('<yourAccessKeyId>', '<yourAccessKeySecret>')
# Endpoint以杭州为例,其它Region请按实际情况填写。
bucket = oss2.Bucket(auth, 'http://oss-cn-hangzhou.aliyuncs.com', '<yourBucketName>')

# <yourLocalFile>由本地文件路径加文件名包括后缀组成,例如/users/local/myfile.txt
bucket.put_object_from_file('<yourObjectName>', '<yourLocalFile>')
# 设置上传文件的访问权限
bucket.put_object_acl(fileName, oss2.OBJECT_ACL_PUBLIC_READ)

官方 API 的快速开始中没有介绍对上传的文件进行权限访问设置操作。一定要设置权限访问,不然无法获得一个永久性的可读 url

  • 根据阿里对上传文件的规则生成上传文件的 url

主要的规则如下:

<scheme>://<yourBucketName>.<RegionName>/<fileNameUrlEncode>
http://jhzhang-blog.oss-cn-shanghai.aliyuncs.com/30c3536bb2d774d46fc16fb9e0795226

- scheme:通常就是 http 协议,可以根据 bucket._make_url.scheme 属性获得
- yourBucketName:就是你前面创建的 bucket 对象的名称,通过 bucket.bucket_name 获得
- RegionName: 就是这个区域所划分的域名,通过属性 bucket._make_url.netloc 获得。
- fileNameUrlEncode:就是我们上传文件名的 urlencode 编码

主要的内容代码如下,需要导入 from urllib import parse 模块。

	# 上传文件
	# <yourLocalFile>由本地文件路径加文件名包括后缀组成,例如/users/local/myfile.txt
	callback = bucket.put_object_from_file(fileName, filePath)
	# 设置上传文件的访问权限
	bucket.put_object_acl(fileName, oss2.OBJECT_ACL_PUBLIC_READ)
	# 进行url 编码
	newFileName = parse.quote_plus(fileName)
	url = "{}://{}.{}/{}".format(bucket._make_url.scheme, bucket.bucket_name, bucket._make_url.netloc, newFileName)

PS: 为了避免文件的重复,我们可以考虑将文件内容的hash值作为我们上传文件的文件名。

优化本地的 Markdown 编辑体验

  1. 打造一个Markdown本地编辑器
  2. 实现拷贝图片自动的上传到阿里云图床,并且插入到本地编写的Markdown文件中

1、 主要的问题在于如何在Markdown中导入第三方的python模块,并且使用

其实考虑清楚Python的运行原理可以清楚地知道,python也是在 sys.path 中寻找指定名称的模块。所以,需要导入第三方模块,就需要告诉 python 在那些路径上寻找相关的python模块代码。
具体分析,打印出Python的系统路径就可以看到导入的模块存在哪些问题

import sys
print(sys.path)

为了方便,我在本地的 anaconda 中安装了阿里的 OSS2 模块,但是 sublime 使用的是一个隔离的 python 模块。所以需要制定 sublime 自带的 python 环境使用 anaconda 模块的内容。

在 sublime 插件的模块中,导入相关的模块的模块的路径,我这里导入了所有的 anaconda 模块路径。

import sys
# 加载第三方的模块
sys.path.append("D:/ProgramData/Anaconda3/Lib/site-packages")

ps:建议将模块 cropty 模块中的所有 .cp37-win_amd64.pyd 模块的文件都重命名为 .pyd,因为不这样的话在导入模块的时候可能会存在相关问题。如果在 sublime 中使用未出现问题最好。

  1. 在 sublime 中调试图片插入代码?

参考我的另外一篇文章可以看到,实战:在 sublime text3 中开发一个 markdown 图片插入插件,可以看到如何插入本地的图片到 Markdown 文档中。

然后就是加上一个编写本地文件的脚本进行上传操作。为了解决文件内容重复的问题,会将图片内容在本地进行一个 hash 计算,求出内容的 hash 值作为上传文件的文件名。

sublime 中代码实现如下:(相关的配置内容会写入到 markdown.sublime-settings 文件中)

import sublime
import sublime_plugin
import os
import os.path
import sys
import shutil
import re
import sys
import time
import hashlib
# 加载第三方的模块
sys.path.append("D:/ProgramData/Anaconda3/Lib/site-packages")
import oss2
from urllib import parse
# 获得配置信息
settings = sublime.load_settings('markdown.sublime-settings')

def get_bucket():
	'''
	获得默认的阿里云的 oss bucket 模块
	:param yourAccessKeyId:
	:param yourAccessKeySecret:
	:param yourBucketName:
	:return: 返回默认的 bucket 模块
	'''

	# 判断账号密码是否为空
	yourAccessKeyId = settings.get("yourAccessKeyId")
	if not yourAccessKeyId:
		print("yourAccessKeyId is none in markdown.sublime-settings file")
	yourAccessKeySecret = settings.get("yourAccessKeySecret")
	if not yourAccessKeySecret:
		print("yourAccessKeySecret is none in markdown.sublime-settings file")	

	yourBucketName = settings.get("yourBucketName")
	if not yourBucketName:
		print("yourBucketName is none in markdown.sublime-settings file")

	# 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建RAM账号。
	auth = oss2.Auth(yourAccessKeyId, yourAccessKeySecret)
	# Endpoint以杭州为例,其它Region请按实际情况填写。
	bucket = oss2.Bucket(auth, 'http://oss-cn-shanghai.aliyuncs.com', yourBucketName)
	if not bucket:
		print("bucket is none, will create new bucket")
		# 设置存储空间为私有读写权限。
		bucket.create_bucket(oss2.models.BUCKET_ACL_PRIVATE)
	return bucket

def get_File_Md5(filename):
	"""
	获得文件内容的Hash值
	:param filename:  文件路径
	:return: 该文件内容的hash值
	"""
	if not os.path.isfile(filename):
		return
	myhash = hashlib.md5()
	f = open(filename, 'rb')
	while True:
		b = f.read(8096)
		if not b:
			break
		myhash.update(b)
	f.close()
	return myhash.hexdigest()

class InsertPictrueCommand(sublime_plugin.TextCommand):

	def up_and_create_url(self,filePath, bucket=get_bucket()):
		'''
		上传一个指定的路径的文件到指定的bucket模块中,设置为只读权限
		:param filePath: 本地文件路径
		:param bucket: bucket对象
		:return: 上传后的url链接
		'''
		absPath = os.path.abspath(filePath)
		fileName = os.path.basename(absPath)
		# 如果文件不存在
		if not os.path.exists(filePath):
			print("file path not exist ", filePath)
			return
		# 上传文件-在文件名前面加上当前时间,防止文件重复
		# nowTime=time.strftime('%Y%m%d%H%M%S',time.localtime(time.time()))
		fileName = get_File_Md5(absPath)
		# <yourLocalFile>由本地文件路径加文件名包括后缀组成,例如/users/local/myfile.txt
		callback = bucket.put_object_from_file(fileName, filePath)
		# 设置上传文件的访问权限
		bucket.put_object_acl(fileName, oss2.OBJECT_ACL_PUBLIC_READ)
		# 进行转码
		newFileName = parse.quote_plus(fileName)
		url = "{}://{}.{}/{}".format(bucket._make_url.scheme, bucket.bucket_name, bucket._make_url.netloc, newFileName)
		return url

	"""插入图片"""
	def run(self, edit):
		# 获得当前编辑窗口对象
		window = self.view.window()
		# 当前文件名
		current_file_name = self.view.file_name()
		current_file_extension = window.extract_variables()["file_extension"]
		if(not current_file_extension in ["md"]):
			print("插入文件名不是 markdown 类型文件")
			return

		# 当前的 sheet
		current_sheet = window.active_sheet()
		# 打开对话框选择文件
		window.run_command("prompt_open_file",{"initial_directory":"C:/Users/Public/Pictures"})


		# 查找文件,并且关闭
		file = None
		window = self.view.window()
		for sheet in window.sheets():
			# 强制切换到当前 sheet 否则,无法获取文件名
			window.focus_sheet(sheet)
			# 获取当前 sheet 的文件后缀
			try:
				# 如果没有后缀的话,会报错
				file_extension = window.extract_variables()["file_extension"]
			except Exception as e:
				continue

			if(file_extension in ["jpg","png","gif","bmp"]):
				file = window.extract_variables()["file"]
				file_base_name = window.extract_variables()["file_base_name"]
				window.run_command("close")

		# 激活之前编辑的窗口
		window.focus_sheet(current_sheet)

		if file and file_base_name:
			ismove=settings.get("ismove") # 判断当前的获得图片是否进行移动
			if ismove:
				# 图片将要存储的目录
				dir_pic = self.getPicturePath(current_file_name)
				# 移动文件到相对目录下
				rel_path = self.movePicToRelPath(file,dir_pic)
			else:
				# 阿里云链接
				rel_path = self.up_and_create_url(file)
			self.view.run_command("insert_snippet",
				{
					"contents": "![%s](%s)" % (file_base_name, rel_path) 
				}
			)

	def getPicturePath(self,file_path):
		"""

		"""
		# 获得文件名
		file_name = os.path.basename(file_path)
		# 文件夹名
		dir_name = os.path.dirname(file_path)
		# 图片存放在相对路径中,新建同名的文件夹+.assert
		dir_pic_name = file_name.replace(" ","_").replace(".md",".image")
		dir_pic = os.path.join(dir_name,dir_pic_name)
		# 如果不存在,则新建
		if(not os.path.exists(dir_pic)):
			os.mkdir(dir_pic)
		return dir_pic

	def movePicToRelPath(self,file_path, dist_dir_path):
		"""将图片移动到指定目录 文件 → 文件夹, 并且返回移动后的相对路径"""
		try:
			shutil.move(file_path, dist_dir_path)
			print("move file: ",file_path," → ",dist_dir_path)
		except Exception as e:
			print("存在相同文件")
			pass
		file_name = os.path.basename(file_path)
		dir_name = os.path.basename(dist_dir_path)
		return dir_name+"/"+file_name

猜你喜欢

转载自blog.csdn.net/u013019701/article/details/83155501
今日推荐