参考:
命令格式–对象存储-火山引擎
https://www.volcengine.com/docs/6349/152744
本文所有代码已在centos6测试通过。
本文包括两个部分。第一部分是火山云的基本使用方法,第二部分是通过python脚本获取云上某指定路径下的文件信息(准确的说是文件名)。本来还想写怎么异步下载的,想了下没啥好说的,就是替换一下命令,原理是一样的,所以不写了。
问题描述
接到测序公司通知,下机数据要从原来的阿里云迁到火山云,所以我这边要调整订单号预测程序和异步下载程序。正好之前也没用过火山云,大概写一下存档。
火山云使用方法
详细的使用方法参见官方文档,此处只写我用过的、测试过的功能。
安装与配置
Linux,centos6:
wget https://tos-tools.tos-cn-beijing.volces.com/linux/tosutil
chmod a+x tosutil
查看版本:
./tosutil version
配置方法:
./tosutil config -i AccessKeyId -k AccessKeySecret -e Endpoint -re Region
其中AccessKeyId,AccessKeySecret,Endpoint和Region都是对方公司给出的。国区是cn-shanghai。
查看其他配置方法:
./tosutil help config
需要其他帮助:
./tosutil help
列出文件
./tosutil ls tos://xxxxx
打印的信息大概是:
Start at 2024-07-30 01:46:51.284200948 +0000 UTC
Listing objects .
Object list:
key LastModified Size StorageClass ETag
tos://xxx/xx.fastq.gz 2024-07-2xxx xxGB STANDARD "xxx"
...
Total size of prefix [xxxx] is: xxGB
File number is: x
下载文件
单个文件下载:
./tosutil cp -j 12 -p 12 tos://xxx ./
下载文件夹需要多一个-r
./tosutil cp -r -j 12 -p 12 tos://bucket/folder ./folder_url
调用python获取文件前缀
问题
列出云上某路径下的所有文件后,需要读取每个文件名称的一部分,作为待创建的订单的订单号。
其实不通过python也可以,因为历史遗留,所以不动代码了。。就用java解析也挺好的。
思路
前台设计一个按钮,点击后向后台发出异步的post请求,后台收到后解析表单信息,获取火山云的url链接,然后传递给python脚本,解析后返回订单号。
实现
前台
发送请求:
// 预测样本编号
$("#predictSampleIdsButton").click(function(){
var formData = new FormData();
formData.append("myOrder", $("#addOrUpdateForm").serialize());
$.ajax({
type: "post",
url: "/seq_tool/predict",
data: formData,
processData: false, // jQuery不要处理发送的数据
contentType: false, // jQuery不要设置Content-Type请求头
success: function (json) {
// 解析结果,前端做一些效果,略
},
error: $.tool.ajaxError,
});
});
后台
处理post请求:
//@RequiresPermissions("...")
@PostMapping(value="/predict")
@ResponseBody
/**
* 普通用户创建测序订单时预测样本编号
* @param myOrderJson json格式的参数
* @return
*/
public String predictSampleIds(@RequestParam("myOrder")String myOrderJson) {
String msg = "";
// 处理json里其他信息
// dataUrl解析自myOrderJson
List<String> specimenIds = new ArrayList<String>();
ListenableFuture<String> result = this.asyncService.getSpecimenIds(dataUrl);
result.addCallback(new SuccessCallback<String>() {
@Override
public void onSuccess(String result) {
specimenIds.add(result);
}
}, new FailureCallback() {
@Override
public void onFailure(Throwable ex) {
System.out.println(ex.getMessage());
}
});
msg = specimenIds.get(0);
return msg;
}
其中this.asyncService
是一个包含很多异步操作的服务,getSpecimenIds()
这样实现:
public ListenableFuture<String> getSpecimenIds(String dataUrl) {
String message = "";
// 需要判断来自哪个云
String choice = "";
if(dataUrl.startsWith("tos")) {
choice = "1"; // 1 表示来自火山云
} else if (dataUrl.startsWith("oss")) {
choice = "0"; // 0 表示来自阿里云,目前也代表其他
}
// 这里使用单引号是因为dataUrl里可能存在特殊字符分号
String cmdStr = String.format("cd /xxxx && python getSid.py '%s' %s", dataUrl, choice);
String[] cmds = new String[] {"sh", "-c", cmdStr};
Process pcs = null;
StringBuilder sb = new StringBuilder();
try {
pcs = Runtime.getRuntime().exec(cmds);
BufferedReader stdInput = new BufferedReader(new InputStreamReader(pcs.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(pcs.getErrorStream()));
String s = null;
//打印出输出结果
//log.info("标准输出命令");
while ((s = stdInput.readLine()) != null) {
// 添加id
sb.append(s);
}
//log.info("标准错误的输出命令");
while ((s = stdError.readLine()) != null) {
//log.info(s);
}
// 会一直等待,所以需要在finally块中杀死进程
pcs.waitFor();
} catch (IOException e) {
log.error(e.toString());
} catch (Exception e) {
log.error(e.toString());
}
finally {
pcs.destroyForcibly();
}
message = sb.toString();
return new AsyncResult<String>(message);
}
python
最后,关于getSid.py
,要获取标准输出可以使用:
import commands
status2, output2 = commands.getstatusoutput("./tosutil ls %s" % link)
此处link是云上的路径,我的python版本是2.7。
另外,如果传进来的某个sys.argv[i]
是包含分号;
的,还是建议传的时候用单引号包裹这个参数,避免错误解析。
比如我一次性传入多个链接,如下面这种时,
python getSid.py tos://链接一;tos://链接二 1
会报错:
bash: tos://链接二: No such file or directory
此时出错的原因就是分号错误解析了,改成下面这样就没问题了。
python getSid.py 'tos://链接一;tos://链接二' 1