# aardio 范例: 对象存储



```aardio
import win;
import console.int;
import web.rest.qiniu;  // 管理端库
import web.rest.client; // 通用客户端库
import fsys;

/*
要注意七牛 kodo 对象存储分为两个角色。
执有管理密钥的 web.rest.qiniu 对象 —— 属于管理角色。
执有临时上传下载密钥的 web.rest.client 对象 —— 属于传输角色

如果用 web.view 做界面，则可以由网页 JavaScript 充当传输角色，
只要把生成的临时上传下载链接与 token 扔给前端即可。

如果是 HTTP 服务端与客户端，则用执有管理密钥的 web.rest.qiniu 写服务端代码，
生成的临时上传下载链接与 token 扔给浏览器前端即可。

一定要将这两个不同职责的角色分清楚。
*/

//配置
var config = {
	accessKey = "你的AccessKey";
	secretKey = "你的SecretKey";
	bucket    = "你的Bucket名称"; 
	domain    = "http://你的域名.bkt.clouddn.com"; // 必填，下载用
	// 上传节点：华东 up.qiniup.com, 华北 up-z1.qiniup.com 等
	uploadUrl = "http://up.qiniup.com"; 
}

console.assert(config.accessKey != "你的AccessKey","请配置 AccessKey");

// ==========================================
// 角色一：服务端 / 管理员 (Manager)
// 职责：持有密钥，负责发号施令、生成凭证、管理文件
// ==========================================
console.log("【角色：管理端】正在初始化...");
var qiniuMgr = web.rest.qiniu(config);

// 1. 生成 Upload Token 
var uploadToken = qiniuMgr.getUploadToken({
    scope = config.bucket;
    // 强制七牛返回 JSON 格式，包含 key 和 hash
    returnBody = '{"key":"$(key)","hash":"$(etag)","fsize":$(fsize)}';
});

// 准备一个测试文件
var localFile = "/qiniu_test_client.txt";
string.save(localFile, "Test file from aardio client " + tostring(time.now()));


// ==========================================
// 角色二：客户端 / 前端 (Client)
// 职责：不接触 SecretKey，只拿 Token 干活
// ==========================================
console.log("【角色：客户端】准备上传...");

var uploadClient = web.rest.client();
var uploadApi = uploadClient.api(config.uploadUrl);

// 引入控制台进度条组件
import console.progress;
var bar = console.progress(); 

var result, err = uploadApi.sendMultipartForm({
	token = uploadToken;
	key   = "client/test.txt";
	file  = "@" + localFile;//上传文件路径第一个字符必须是 "@" 
},function(sendData,sendSize,contentLength,remainSize){
	// 使用进度条组件显示
	var percent = math.round((sendSize/contentLength)*100);
	bar.setProgress(percent, string.format("上传中... %d%%", percent));
});
bar.reset(); // 上传完成后重置进度条
console.log(); // 换行

console.status(result,"上传",,,err);

// ==========================================
// 角色一：服务端 / 管理员 (Manager)
// 职责：验证上传结果，生成下载链接
// ==========================================
console.log("------------------------------------------");
console.log("【角色：管理端】验证文件信息 (Stat)...");

// 管理 API 地址 (华东: rs.qiniu.com)
var rsApi = qiniuMgr.api("http://rs.qiniu.com");

// 构造 EncodedEntryURI (Bucket:Key 的 Base64)
var entryId = qiniuMgr.entry(config.bucket, "client/test.txt");

// 获取文件信息: GET /stat/<EncodedEntryURI>
// aardio rest 库会自动将 [] 内的参数拼接到 URL 路径中
var statInfo, err = rsApi.stat[entryId].get();

if(statInfo){
	console.log("【角色：管理端】文件存在，Hash:", statInfo.hash);
	
	// 生成私有下载链接（即使是公开 Bucket，用私有链接也是安全的）
	var downloadUrl = qiniuMgr.getPrivateDownloadUrl(config.domain, "client/test.txt", 300);
	console.log("【角色：管理端】签发下载链接(5分钟有效):", downloadUrl);
	
	// ==========================================
	// 角色二：客户端 / 前端 (Client)
	// 职责：使用链接下载
	// ==========================================
	console.log("------------------------------------------");
	console.log("【角色：客户端】开始下载...");
	
	var downClient = web.rest.client();
	
	// 下载文件到本地
	var savePath = "/download_test.txt";
	var ok, err = downClient.receiveFile(savePath).get(downloadUrl);
	
	if(ok){
		console.success("【角色：客户端】下载成功，内容预览：");
		console.log(string.load(savePath));
		io.remove(savePath); // 清理下载文件
	} else {
		console.error("【角色：客户端】下载失败", err);
	}
	
	// ==========================================
	// 角色一：服务端 / 管理员 (Manager)
	// 职责：测试完成，清理云端文件
	// ==========================================
	console.log("------------------------------------------");
	console.log("【角色：管理端】清理测试文件...");
	
	console.log("------------------------------------------");
	console.log("【角色：管理端】清理测试文件...");
	
	// 写为 ["delete"] 避免被识别为 HTTP method
	var _, err = rsApi["delete"][entryId].post();//此 API 调用成功无返回值
	 
	if(qiniuMgr.lastStatusCode==200){
		console.success("【角色：管理端】删除成功。");
	} else {
		console.error("删除失败", err);
	}

} else {
	console.error("【角色：管理端】未找到文件", err);
}

// 清理本地上传用的临时文件
io.remove(localFile);
console.pause();
```

