图片上传图床与备份回退策略

前往原站点查看

2022-04-13 01:17:33

今天完成了服务器端的图片上图床、备份、增加记录、获取列表功能。

主要完成的技术要点是对接图床,并且在本地备份(相对于服务器的本地),然后记录计入数据库。

设计流程

先来展示一下我设计的基本流程:

整个流程中,为了尽量让出现异常错误产生的“垃圾”文件数据进行回退,从而保证资源利用率。

图床api的包装与修改

在原有官方提供的java版本api后,经过简单修改即可上传文件。这里有一部分涉及到oauth授权。

public static JSONObject dogeAPIGet(String apiPath, byte[] bytes) throws IOException, JSONException {
	
	String signStr = apiPath + "\n";
	ByteArrayOutputStream bao = new ByteArrayOutputStream();
	bao.write(signStr.getBytes());
	bao.write(bytes);
	byte[] res = bao.toByteArray();
	bao.close();

	String sign = "";
	try {
		Mac mac = Mac.getInstance("HmacSHA1");
		mac.init(new SecretKeySpec(secretKey.getBytes(), "HmacSHA1"));
		sign = new String(new Hex().encode(mac.doFinal(res)), StandardCharsets.UTF_8);
		// Hex 来自 org.apache.commons.codec.binary.Hex
	} catch (NoSuchAlgorithmException | InvalidKeyException e) {
		e.printStackTrace();
		throw new RuntimeException(e.getMessage());
	}
	String authorization = "TOKEN " + accessKey + ':' + sign; // 获取到token

	URL u = new URL("https://api.dogecloud.com" + apiPath);
	HttpURLConnection conn = (HttpURLConnection) u.openConnection();
	conn.setDoOutput(true);
	conn.setRequestMethod("POST");
	conn.setRequestProperty( "Authorization", authorization);
	OutputStream os = conn.getOutputStream();
	os.write(bytes);
	os.close();
	StringBuilder retJSON = new StringBuilder();
	if(conn.getResponseCode() == HttpURLConnection.HTTP_OK){
		String readLine = "";
		try (BufferedReader responseReader=new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
			while((readLine=responseReader.readLine())!=null){
				retJSON.append(readLine).append("\n");
			}
		}
		JSONObject ret = new JSONObject(retJSON.toString());
		if (ret.getInt("code") != 200) {
			System.err.println("{\"error\":\"API 返回错误:" + ret.getString("msg") + "\"}");
		} else {
			JSONObject output = new JSONObject();
			JSONObject data = ret.getJSONObject("data");
			return data;
		}
	} else {
		System.err.println("{\"error\":\"网络错误:" + conn.getResponseCode() + "\"}");
	}
	return null;
}


图片上传图床与备份回退

图片的每一次错误,如果不加以删除,那么后期维护起来也会非常麻烦。

图片如果完全相信图床服务器的运营也是错误的想法,毕竟谁知道哪天倒了(没有咒,求饶)。

所以能上传的图床的前提是本地留有备份和记录。基于这些约束,写了如下代码:

public DogeResult uploadFile(MultipartFile file, Integer aid) {
	DogeResult result = new DogeResult(-1);

	String targetPathUri = "img/" + aid + "/" ; // => img/2/
	String filename = System.currentTimeMillis() + ".png"; // => 45648945123.png

	String localPath = properties.getLocalImgPath() + targetPathUri; // =>; C:/data/dreamcenter/img/2/
	File f = new File(localPath);
	f.mkdirs();

	// save to local path => -1
	try {
		file.transferTo(new File(localPath + filename));
	} catch (IOException e) {
		e.printStackTrace();
		return result;
	}

	// read the saved file => -2
	File okFile = new File(localPath + filename);
	FileInputStream fis = null;
	ByteArrayOutputStream bao = null;
	try {
		fis = new FileInputStream(okFile);
		bao = new ByteArrayOutputStream();
		byte[] temp = new byte[1024];
		int len;
		while((len = fis.read(temp))!=-1){
			bao.write(temp,0,len);
		}
	} catch (IOException e) {
		okFile.delete();
		e.printStackTrace();
		result.setErrno(-2);
		return result;
	} finally {
		... // 关闭bao和fis流
	}

	// save to doge => -3 / -4
	try {
		String req = "?bucket=dreamcenter&key=" + targetPathUri + filename;
		JSONObject jsonObject = DogeHelper.dogeAPIGet("/oss/upload/put.json" + req, bao.toByteArray());
		if (jsonObject != null) {
			result.setErrno(0);
			String base = properties.getCloudImgPath();
			String key = jsonObject.getString("key");
			result.addImage(base + key,null,base + key);

			// into database
			Integer res = imageDao.insertImg(new Image(null, aid, filename.split("\\.")[0],
					TimeRelated.getCurrentTime(), base + key, null));
			if (res != 1) {
				okFile.delete();
				DogeHelper.dogeAPIGet("/oss/file/delete.json?bucket=dreamcenter",
						("[\"" + targetPathUri + filename + "\"]").getBytes());
				result.setErrno(-3);
			}
		} else {
			okFile.delete();
			result.setErrno(-4);
		}
	} catch (IOException | RuntimeException | JSONException e ) {
		okFile.delete();
		result.setErrno(-4);
		e.printStackTrace();
	}
	return result;
}


其实在做这个个人主页之前就已经测试了自己能不能接入成功,所以才敢下决定对接的,

最开始是打算接入百度网盘,然而百度只能调取一些基础的服务,图片上传貌似也要“提升权限”!

当时好不容易把百度的分片上传原理和需求进行了java封装,结果告诉我权限不够,

打击还是非常大的,当然后续的仓库模块肯定是要借用百度网盘的,

不过预计也仅仅停留在列表获取和链接下载上罢了。


今天也是有收获的一天,明天继续加油,争取把图片模块(回忆)完成个50%,后天再接入博客和动态,

之后继续优化图片模块的图片展示,最后进行模块访问统计,那么第一阶段就告一段落了,哈哈!

加油!



上一篇: 懒加载与日期文字化
下一篇: 后台回忆页专辑图片陈列