大家好~我是米洛

我正在从0到1打造一个开源的接口测试平台, 也在编写一套与之对应的教程,希望大家多多支持。

欢迎关注我的公众号米洛的测开日记,获取最新文章教程!

回顾

上一节我们把oss运用到了实战之中,并且完成了http请求支持文件上传的需求。由于下一节的内容我还没有想好,想着先把免费的gitee oss搞定再说。

但在编写代码的过程中发现许多问题或者说,让我有些怀疑这块是不是该收手了。

了解gitee api

gitee这样相对比较大的平台,一般都会对外提供api操作对应的仓库数据。

他们的文档地址: https://gitee.com/api/v5/swagger#/getV5ReposOwnerRepoStargazers?ex=no

测试平台系列(93) 免费的gitee当oss真的香吗 - 图1

我们可以看到,它的api甚至都已经是第五个版本了(v5代表的是api的版本)。

所有的api都可以从这个网站查询到,话不多说,我们赶紧找找自己想要的api: 查询仓库的文件下载文件, 新增文件, 删除文件

找了一圈却发现,gitee没有提供类似os.walk或者能够获取到所有目录/文件的api,只有下面这种:

测试平台系列(93) 免费的gitee当oss真的香吗 - 图2

也就是说,如果我们是深层次目录,可能要不断遍历下去。

但如果我们限制下用户使用的时候,不嵌套那么多目录,应该也还能凑合用,毕竟没有什么比免费更香了。

编写客户端获取部分

  • 研究下gitee配置和aliyun的不同
    在gitee里面,只用3个数据就能确定一个项目:
    1. access_token(用来判断你是哪个用户)
    2. repo(仓库)
    3. owner(用户)

测试平台系列(93) 免费的gitee当oss真的香吗 - 图3

所以我们把endpoint这个置空,毕竟用不到,其他的一次为access_token, ownerrepo

  • 改写oss获取客户端的方法

测试平台系列(93) 免费的gitee当oss真的香吗 - 图4

如果类型是GITEE,则返回GiteeOss,当然此时GiteeOss里面具体的方法还没有实现。

注意这里我在Config里头加入了GITEE和ALIYUN变量,如果报找不到变量的错,是因为我封装了一下,希望大家能够自己发现。

由于教程不是纯新手教程,在发现我留的坑或者代码悄悄改动的时候,也是大家进步的时候,因为不需要作业,天然给你留了作业。(机智的我)

实现GiteeOss中的方法

  • 初始化方法

测试平台系列(93) 免费的gitee当oss真的香吗 - 图5

值得注意的是,我设置了个base_path,是为了能够把pity搞的oss文件全部放入pity文件夹。其他的就是gitee的基础api地址。

  • 创建文件
  1. @aioify
  2. async def create_file(self, filepath: str, content: bytes):
  3. gitee_url = self._base_url.format(self.user, self.repo, self._base_path, filepath)
  4. data = base64.b64encode(content).decode()
  5. json_data = {"access_token": self.token, "message": "pity create file", "content": data}
  6. r = requests.post(url=gitee_url, json=json_data)
  7. if r.status_code == 400:
  8. raise Exception("文件重复,请先删除旧文件")
  9. if r.status_code != 201:
  10. raise Exception("上传文件到gitee失败")

注意这里有个aioify装饰器,这需要额外安装aioify包,然后这个方法就能被异步执行,还是挺好用的。

其实没有很复杂的内容,就是调用gitee api的接口,去上传文件,如果http状态码是400说明文件重复,201说明成功(虽然我也很纳闷为什么不是200)。

  • 删除文件
  1. @aioify
  2. async def delete_file(self, filepath: str):
  3. filepath, sha = filepath.split("$")
  4. gitee_url = self._base_url.format(self.user, self.repo, self._base_path, filepath)
  5. r = requests.delete(gitee_url, params=dict(access_token=self.token, message="delete file", sha=sha))
  6. if r.status_code != 200:
  7. raise Exception("刪除文件失败")
  • 获取文件列表
  1. @aioify
  2. async def list_file(self):
  3. ans = list()
  4. await self.query_file_by_path("", ans)
  5. return ans
  6. @aioify
  7. async def query_file_by_path(self, filepath: str, ans: List[dict]):
  8. gitee_url = self._base_url.format(self.user, self.repo, self._base_path, filepath)
  9. r = requests.get(gitee_url, params=dict(access_token=self.token))
  10. file_list = r.json()
  11. for f in file_list:
  12. if f.get("type") == "file":
  13. ans.append(dict(key=f.get('path')[5:], download_url=f.get("download_url"),
  14. size=0, sha=f.get("sha"),
  15. last_modified=None, type='gitee'))
  16. continue
  17. await self.query_file_by_path(f"{filepath}/{f.get('name')}", ans)

这边首先获取根目录下的所有文件,接着判断文件列表,如果是目录则继续获取该目录下的文件,否则把文件数据放入ans,最终返回ans即可。

要注意的是,gitee的文件下载需要用到sha字段,而且gitee不支持查看最后编辑时间文件大小,这2点也是鸡肋的点。

  • 下载文件

测试平台系列(93) 免费的gitee当oss真的香吗 - 图6

大家可以看看调用下载目录后,返回的data数据。我这里简单讲下,他其实是一个json,并不是文件流。

  1. {"content": "文件base64编码的byte字符串", "size": "文件大小"}

所以我们取出来数据后要继续用base64decode,最后写入我们生成的临时文件,通过fastapi的FileResponse提供下载功能。

总结

gitee最大特点就是免费,作为oss实在是不方便,作为图床倒是不错,目前也没有防盗链这一说。

  • 获取文件不方便
  • 不能获取文件大小,除非你下载文件
  • 没有更新时间
    所以最后咱们oss的管理页面长这样:

测试平台系列(93) 免费的gitee当oss真的香吗 - 图7

但其实是我们偷懒,如果在文件上传的时候我们记录一份文件相关数据到本地,那这些都不是问题。那应该还是香的吧!(但又可能存在数据不同步问题)

怎么说呢,一开始是懒,看来还是付出代价了这个坑先放着,以后再填吧