这个作业属于哪个课程 | 2301-计算机学院-软件工程社区-CSDN社区云 |
---|---|
这个作业要求在哪里 | 团队作业——站立式会议+alpha冲刺-CSDN社区 |
这个作业的目标 | 记录alpha冲刺Day9 |
团队名称 | 熬夜会秃头 |
团队置顶集合随笔链接 | Alpha 冲刺随笔置顶(熬夜会秃头团队)-CSDN博客 |
目录
一、团队成员站立式会议总结
组员 | 昨天站立式会议到现在的进展: | 存在的问题/遇到的困难: | 今天到明天会议的安排: | 心得体会: |
陈少桐 | 实现了图片的选择功能,并且编写了缺少的页面 | 在引入昨天的uview组件包的时候会报错:,然后利用npm重新下载了一下,并且配置了以来,但是新报错,于是去查找资料,发现需要下载vite,但是自己的项目明明创建的时候没有利用vite,想了想错误跟网上的一样,于是学着自己搭建依赖,但是问题来了,搭建完依赖错误变得更多了。于是就把新下载的vite删除了,然后就回到源代码去看,发现底下的scss颜色不一样,于是往前面的mehod看,发现少了一个}。。。至此问题解决 | 编写缺少的页面以及子窗体以及图片上传功能 | 遇到bug的时候不要死盯着报错问题,而是需要查找资料,结合实际,回到自己写的代码中观察他有什么错误,说不定一些很奇怪的错误就是因为一个小符号导致的 |
梁菲汎 | 实现了各页面之间的点击跳转 | 无 | 完善前端功能 | 首先需要对按钮绑定一个函数,然后在函数里进行页面路由的改变。 这里要确保项目中已经在使用vue-router。 |
陈知菲 | 实现了文件上传功能,实现了Redis的slot分配在文件上传中的运用,使集群中文件分散在各节点中存储,减轻了单节点的存取负担 | 单节点的存取负担减轻,但由于集群的不稳定性,文件容易丢失损坏 解决:采用纠删码算法,当文件上传至某一节点后,将其用纠删码编码后分成n + m份传输至其他节点存储,若文件丢失损坏,从其他节点拉取分片,只要其他节点的丢失分片数<m,则可恢复整份文件(待实现) | 文件分片上传的分布式设计与运用,下载文件、删除文件功能的实现 | 对Redis的无中心化集群搭建、slot分配机制有了更深的理解,学会了如何将slot的理论知识用代码表达,并运用到具体功能的优化上,离去中心化集群的搭建更进一步 |
李恒欣 | 学习Redis和slot分配相关知识 | 接触新的内容且需要学习的内容较多,压力较大 | 继续完善后端代码功能 | 初步学习和了解了Redis和slot分配相关知识 |
邱思源 | 进行后端代码单元测试 | 执行大量测试样例时出现执行速度较慢问题,通过并发测试后得以解决 | 对新实现的后端代码进行单元测试 | 学习并使用了并发测试,提高了单元测试的效率 |
宋芳鑫 | 编写测试随笔的大体框架 | 无 | 完善测试随笔、编写冲刺总结随笔 | 在编写随笔的时候也能从中学到关于测试的一些知识 |
张一凡 | 进行前端代码单元测试 | 前端代码通常需要考虑用户的输入和操作,而这些行为很难在单元测试中进行模拟 | 对新实现的前端代码进行单元测试 | 对前端测试日益得心应手 |
林承桢 | 对新完成的模块进行功能测试,将完成的测试结果写入测试文档 | 有时不确定是否能够涵盖全部的应用场景 | 继续进行功能测试,完善测试文档 | 只有在测试时尽量全面地涵盖所有可能出现的情况才能保证项目获得最好的效果,让用户获得好的体验 |
黄才栋 | 搭载了vue-cli,并且对前端的一些代码进行了测试 | 无 | 完善前端功能 | 感受了利用vue脚手架的便捷性以及学到了前端代码的编写规范 |
谢怀广 | 核对已完成代码规范并制作项目燃尽图 | 需要不断学习新的知识技能,学习的范围较广,不够深入 | 核对新增代码规范并制作项目燃尽图 | 在核对代码规范和制作燃尽图的过程中,可以发现自己的技能还有待提高。着能让我持续学习新的知识以更好地完成工作 |
二、今日成果展示
图片选择:
takePhoto(){
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['camera','album'],
success: (res)=> {
console.log(res);
const tempFilePaths = res.tempFilePaths;
}
});
新的页面:
页面点击跳转:
export default {
name :'login',
setup()
{
...
const jump = () =>{
router.push({path:'/register'})
}
return {jump}
}
}
单文件上传接口:
@PostMapping("/uploadSimple")
public ResponseResult<String> uploadSimple(@RequestParam MultipartFile file,
@RequestParam String originMd5,
@RequestParam String bucketId,
@RequestParam Boolean isZip,
@RequestParam Integer isInternal) {
SimpleFile simpleFile = new SimpleFile(file, originMd5, bucketId, isZip);
if (isInternal == 1) {
return putService.uploadSimple(simpleFile);
} else {
return cPutService.uploadSimple(simpleFile);
}
}
本地实现
对于较小的文件,先将前端上传的文件存在内存中,调用异步方法写入磁盘,加快响应速率
@Override
public ResponseResult<String> uploadSimple(SimpleFile file) {
String fileName = file.getMultipartFile().getOriginalFilename();
Integer res = fileUtil.checkFile(file);
if (res == -2) {
return new ResponseResult<>(200, "文件已存在");
} else if (res == -3) {
return new ResponseResult<>(200, "文件内容缺失");
}
String path;
String key;
if (file.getIsZip()) {
assert fileName != null;
path = FileProperty.realPath +
file.getBucketId() + "/" + fileName.substring(0, fileName.lastIndexOf('.')) + ".zip";
key = "zipStoreCache::" + path;
} else {
path = FileProperty.realPath +
file.getBucketId() + "/" + fileName;
key = "normalStoreCache::" + path;
}
String finalKey = key + "/" + FileVersion.addVersion(key);
boolean success = redisUtil.storeFile(finalKey,
file.getMultipartFile(), Duration.ofMinutes(10L));
if (success) {
asyncTask.redis2Local(file.getBucketId(), finalKey, fileName);
return new ResponseResult<>(200, "已上传");
} else {
return new ResponseResult<>(400, "上传失败");
}
}
比对前后端的md5实现文件的校验和去重(不可上传内容完全一致的文件)
public Integer checkFile(SimpleFile simpleFile) {
//重复上传
String md5 = getMd5(simpleFile.getMultipartFile());
Map<String, String> md5Set = md5Cache.getMD5SetByName(simpleFile.getBucketId(), 1);
Set<Object> keys = template.keys("normalStoreCache::"
+ FileProperty.realPath + simpleFile.getBucketId() + "/*");
if (keys != null) {
for (Object key : keys) {
String content = (String) template.opsForValue().get(key);
ByteArrayInputStream bis = new ByteArrayInputStream(Base64.getDecoder().decode(content));
try {
String strKey = (String) key;
String redisMd5 = doGetMd5(bis);
String totalFilePath = strKey.substring(strKey.indexOf("::") + 2);
String filePath = totalFilePath.substring(0, totalFilePath.lastIndexOf("/"));
md5Set.put(redisMd5, filePath);
} catch (IOException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}
if (md5Set.containsKey(md5)) {
return -2;
}
//内容校验错误
if (!md5.equals(simpleFile.getOriginMD5())) {
return -3;
}
return 1;
}
每一个节点对于上传的文件,求其文件名的hash值,判断其应分配到哪个slot,进而判断该slot属于哪个主机(拉取Nacos的服务列表查询),使用Feign动态改变URL,将文件发送至指定的节点存储,指定节点接收后调用上述putService的uploadSimple方法,将文件存至本地
@Override
public ResponseResult<String> uploadSimple(SimpleFile simpleFile) {
String bucketId = simpleFile.getBucketId();
String filename = simpleFile.getMultipartFile().getOriginalFilename();
assert filename != null;
String uri = servicesUtil.distributeURI(filename);
if ("localhost".equals(uri) || uri == null) {
return putService.uploadSimple(simpleFile);
} else {
try {
return testFeign.uploadSimple(new URI(uri), simpleFile.getMultipartFile(), simpleFile.getOriginMD5(),
bucketId, simpleFile.getIsZip(), 1);
} catch (URISyntaxException e) {
return new ResponseResult<>(500, "上传错误", e.getMessage());
}
}
}
Slot分配算法
public String distributeURI(String str) {
int hashCode = str.hashCode();
hashCode %= 150;
if (hashCode >= slot && hashCode < slot + 50) {
return "localhost";
}
List<ServiceInstance> instances = getInstance("node01-service");
for (ServiceInstance instance : instances) {
Integer slot1 = getSlot(instance);
if (hashCode >= slot1 && hashCode < slot1 + 50) {
return "http://" + instance.getHost() + ":" + instance.getPort();
}
}
return null;
}