个人Blog:dykang.top
描述:
前端传输数据给服务端,服务端保存数据库后将数据渲染到freemark文件内,生成静态文件html文件,将静态文件保存到minio中,将对应的静态文件的minio中地址保存到数据库中,来访问这个博客文章
遇到的问题(Non-XML response from server问题)
yml配置
要记住新版本minio和老版本一点也不同!!!尤其是yml相关配置~~~去看yml注释!!!!
老版本配置教程
-
首先docker拉取镜像
docker pull minio/minio:RELEASE.2021-06-17T00-10-46Z
-
构建容器
docker run -p 9000:9000 --name minio -di --restart=always \ -e "MINIO_ROOT_USER=minio" \ -e "MINIO_ROOT_PASSWORD=minio@123" \ -v /usr/local/minio/data:/data \ -v /usr/local/minio/config:/root/.minio \ minio/minio:RELEASE.2021-06-17T00-10-46Z server /data
-
访问 ip + 端口号
思路实现
-
引入依赖
<dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>7.1.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency>
-
yml配置
minio: accessKey: minio secretKey: minio@123 bucket: articles #老版本是登录账号和密码 新版本这俩是service account生成的,不是登录账号和密码 endpoint: http://xxx.xxx.xxx.xxx:9000 #老版本是访问地址 新版本注意是api地址,不是访问网址
-
前端发送请求
{ "id": 1, "title": "123", "content": "dada", "author": "kzy", "url": "" }
-
后端接收请求
@PostMapping("/articles")
public ResponseEntity<String> createArticle(@RequestBody Article article) {
boolean flag = articleRepository.save(article);
Article savedArticle = null;
try {
if (flag) {
savedArticle = articleRepository.selectById(article.getId());
//把数据渲染ftl然后生成静态html文件
articleHtmlService.generateHtml(savedArticle);
// 更新数据库中的URL字段
savedArticle.setUrl(endpoint +"/"+ bucket + "/"+"article"+savedArticle.getId() + ".html");
articleRepository.updateById(savedArticle);
}
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity<>("Error generating HTML", HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<>("Article saved with id: " + savedArticle.getId(), HttpStatus.CREATED);
}
-
创建模板文件
<!DOCTYPE html> <html> <head> <title>${article.title}</title> </head> <body> <h1>${article.title}</h1> <p>${article.content}</p> <footer>Written by ${article.author}</footer> </body> </html>
-
渲染文件和保存到minio中
@Autowired
private ArticleService articleService;
@Autowired
private MinioClient minioClient;
@Autowired
private Configuration configuration;
public void generateHtml(Article article) throws IOException, TemplateException {
Map<String, Object> model = new HashMap<>();
model.put("article", article);
// 使用FreeMarker渲染HTML
//先拿到模板
Template template = configuration.getTemplate("article.ftl");
//在渲染数据,生成静态文件
template.process(model, new FileWriter("d:/article.html"));
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream("d:\\article.html");
// 保存到MinIO
minioClient.putObject(PutObjectArgs.builder()
.contentType("text/html")
.bucket("articles")
.object("article" + article.getId() + ".html")
.stream(fileInputStream, fileInputStream.available(), -1)
.build());
} catch (Exception e) {
e.printStackTrace();
}
}
修改和删除
采用的是删除minio中文件的文件和数据库中的url,并生成新的保存
疑问
删除比较快,但是更新比较慢,那已经删除了,那用户怎么访问到数据呢?
遇到更新操作比删除操作慢,并且担心在更新过程中旧文件被删除后新文件还未生成时,用户可能会访问到不存在的文件,这确实是一个需要考虑的问题。以下是一些策略来解决这个问题:
1. 使用版本控制
MinIO支持对象版本控制,开启版本控制后,删除操作实际上不会立即删除文件,而是将其标记为删除,同时保留历史版本。这样,在新文件生成之前,用户仍然可以访问到旧版本的文件。
2. 临时重定向
在删除旧文件后,可以设置一个临时的重定向机制,将用户请求重定向到一个占位页面或一个加载状态,直到新文件生成并上传成功。
3. 原子操作
确保更新操作的原子性,即要么完全更新成功,要么完全不更新。这通常涉及到事务管理或使用数据库和文件存储的原子操作。
4. 异步处理
将更新操作异步化,首先删除旧文件,然后立即返回成功响应给用户,后台异步生成新文件并上传到MinIO。在新文件生成之前,可以使用缓存或临时文件来处理用户请求。
5. 预生成静态文件
在更新数据库文章内容的同时,预生成新的静态文件并保存在临时位置。在确认新文件已经生成并上传成功后,再删除旧文件。
6. 使用锁机制
在更新过程中,使用锁机制来防止并发访问。当一个文件正在被更新时,其他请求会被阻塞或重定向,直到更新完成。
7. 设置合理的缓存策略
设置MinIO的缓存策略,使得即使文件被删除,用户在短时间内仍然可以从缓存中获取到内容。
8. 使用状态标记
在数据库中为文章设置一个状态标记,如pending_update
,在更新过程中将状态设置为pending_update
,前端根据状态决定是否显示加载提示或重定向。
例子:异步处理
// 更新文章的方法,异步生成和上传新文件 使用缓存和临时文件来访问
public void updateArticleAsync(Long id, Article updatedArticle) {
// 从数据库获取旧文章信息
Article originalArticle = articleRepository.findById(id).orElseThrow();
// 删除旧的MinIO文件
minioClient.removeObject("bucket-name", originalArticle.getUrl());
// 更新数据库,但不保存新的URL
originalArticle.setUrl(null);
articleRepository.save(originalArticle);
// 异步执行生成和上传新文件的操作
executorService.submit(() -> {
String newHtmlContent = generateHtmlContent(updatedArticle);
String newFilePath = "path/to/new/file.html";
// 上传新文件到MinIO
minioClient.putObject("bucket-name", newFilePath, new ByteArrayInputStream(newHtmlContent.getBytes(StandardCharsets.UTF_8)));
// 更新数据库中的URL
updatedArticle.setUrl(newFilePath);
articleRepository.save(updatedArticle);
});
}