用Neo4j建立一个约会网站:第12部分


是时候在我们的约会网站上添加“爱的幻象”了。到目前为止,我们的帖子只是更新了文本状态,虽然有可能爱上某人的话,但如果他们看起来像住在桥下的巨魔,那就更难了。这里有什么计划?良好的...像大多数数据库一样,在Neo4j中存储图像不是一个好主意。相反,我们要存储的是指向映像所在位置的链接,但我们也不希望在文件系统中到处都是映像,然后不得不担心存储空间和复制它们、在地理上分布它们以加快访问速度等问题。托管图像是一个通过使用Content Delivery Networks。因此,让我们利用其中一个来构建我们的功能。

有很多信用违约互换,有些便宜,有些贵,但是我们会选择“没有甜蜜的风险投资钱”的价位和用途BunnyCDN。我喜欢它们的地方是它们很简单。每次我看到自动气象站的仪表盘上有10亿个服务项目,而且不得不把S3和云前线连接到53号公路上,我都觉得有些力不从心。

BunnyCDN提供了一个发送文件的应用编程接口,所以我们将使用Retrofit再次创建一个小界面:

public interface BunnyCDN {

    @PUT("{storageZoneName}/{path}/{fileName}")
    Call<ResponseBody> upload(@Path("storageZoneName") String storageZoneName,
                              @Path("path") String path,
                              @Path("fileName") String fileName,
                              @Body RequestBody body);

在BunnyCDN,我创建了一个名为“五”的存储区,我们将把它用作第一个参数。我们将使用我们的用户的用户名属性作为路径,我们将标准化并清理我们得到的文件名。一会儿再谈。在我们可以使用我们的新界面之前,我们需要创建一个客户端。根据文档,我们需要在标题中传递一个访问密钥,因为我们将发送二进制文件,所以我们在这里将内容类型设置为“八位字节流”。

          OkHttpClient.Builder builder2 = new OkHttpClient.Builder();
          builder2.addInterceptor(chain -> chain.proceed(
                  chain.request().newBuilder()
                          .addHeader("AccessKey", conf.getString("bunny.key"))
                          .addHeader("Content-Type", "application/octet-stream")
                          .build()));
          OkHttpClient client2 = builder2.build();

现在我们可以在改装中使用这个客户端,并将其指向存储网址。

          Retrofit retrofit2 = new Retrofit.Builder()
                  .client(client2)
                  .baseUrl("https://storage.bunnycdn.com/")
                  .build();

          bunny = retrofit2.create(BunnyCDN.class);

好了,随着管道的排除,我们接下来需要编辑我们的帖子模型,以包括一个文件名。为了方便起见,我们将继续添加一种方法来检查帖子是否包含文件。

@Data
public class Post {
    ...
    private String filename;

    public boolean hasFile() {
        return filename != null;
    }

接下来,我们将在“新帖子”的右侧添加一个小相机按钮,这样我们就可以传入文件:

            <label class="btn btn-primary">
                <span class="icon icon-camera">
                    <input name="file" type="file" style="display: none;">
                </span>
            </label>

它看起来像这样:

这将把文件添加到我们的邮件表单中,以便我们处理。在我们的应用程序中,我们将编辑post方法,使用“上传”对象抓取文件,并创建一个请求体发送到BunnyCDN。

        post("/post", req -> {
            CommonProfile profile = require(CommonProfile.class);
            String username = profile.getUsername();

            Upload upload = req.file("file");
            String status = req.param("status").value();
            File file = upload.file();
            RequestBody body = RequestBody.create(MediaType.parse("application/octet"),
                    Files.readAllBytes(file.toPath()));

接下来,我们要做一些有点奇怪的事情。让我们使用当前时间和原始文件名的扩展名为文件创建一个自定义名称。这样我们就不必在网址路径中处理空格或有趣的字符

String time = dateFormat.format(ZonedDateTime.now()) + getFileExtension(upload.name());

这样,我们就可以将文件发送到BunnyCDN,假设它成功了,我们创建一个Post对象,设置状态和文件名(我们省略了URL,只保留最少的),然后通过API在数据库中创建真正的Post。

            Response<ResponseBody> bunnyResponse =
                    App.bunny.upload("fives", username, time, body).execute();
            if (bunnyResponse.isSuccessful()) {
                Post post = new Post();
                post.setStatus(status);
                post.setFilename(username + "/" + time);
                Response<Post> response = App.api.createPost(username, post).execute();
                if (response.isSuccessful()) {
                    return Results.redirect("/user/" + username);
                }

在我们的后端,我们只需要编辑createPost方法,并检查输入中文件名的存在。Neo4j不存储空值,所以如果值不存在,我们就不存储它。或者,我们可以在我们的前端创建时间属性,并将其传递给帖子,然后使用帖子的作者和时间,我们可以重建我们的文件名属性。我认为复杂性不值得节省成本,但如果你正在建立一个像照片分享网站这样的网站,人们上传成千上万张照片,就要记住这一点。

        if (input.containsKey(FILENAME)) {
            post.setProperty(FILENAME, input.get(FILENAME));
        }

回到我们的前端,我们需要编辑帖子的一部分。我们可以检查帖子中是否有文件,如果有,我们就用链接的BunnyCDN拉区的服务器地址给我们的显示添加一个图像。这个网址可以通过添加一个CNAME记录来更改为一个自定义的网址,但是我们暂时不考虑它。

            @if(post.hasFile()) {
                <p>
                    <img src="https://fives.b-cdn.net/@post.getFilename()" style="max-width: 100%" class="rounded">
                </p>
            }

当我们添加一些图片,然后查看我们的帖子时,我们现在可以看到:

如果我们用这个查询来检查我们的Neo4j数据库:

MATCH (n:Post) RETURN n.time, n.status, n.filename LIMIT 25

我们可以看到一些帖子有文件名,而另一些则返回空值。请记住,Neo4j中的每个节点都可以有不同的属性,即使它们是相同的节点“类型”或共享相同的标签。

我们已经走了很远,但仍有大量的工作要做。我们没有低五对帖子应有的“隐藏”效果,我们没有建立任何类型的推荐,我们没有任何消息或电子邮件功能...我想我们会继续下去,但是我们可能会在前进的过程中加入一些关于不同主题的文章。这source code在GitHub上,如果你想得到变化的通知,就把自己添加为“观察者”,或者只是follow me on Twitter进一步更新。