json REST API -文件(即图像)处理-最佳实践

zf9nrax1  于 2023-02-26  发布在  其他
关注(0)|答案(4)|浏览(139)

我们正在用REST API开发服务器,它用JSON接受和响应。问题是,如果你需要从客户端上传图像到服务器。
注意:我还在讨论一个用例,其中实体(用户)可以有多个文件(carPhoto,licensePhoto),也可以有其他属性(名称,电子邮件...),但当您创建新用户时,您不发送这些图像,它们是在注册过程后添加的。
我知道这些解决方案,但每一个都有一些缺陷

1.使用多部分/表单数据代替JSON

  • good*:POST和PUT请求尽可能的RESTful,它们可以包含文本输入和文件。
  • 缺点 *:它不再是JSON,与multipart/form-data相比,JSON更容易测试、调试等
    2.允许更新单独的文件

创建新用户的POST请求不允许添加图像(这在我们的用例中是可以的,正如我在开始时所说的),上传图片是通过PUT请求完成的,作为多部分/表单数据,例如/users/4/carPhoto

  • good*:所有内容(除了文件上传本身)都保留在JSON中,易于测试和调试(您可以记录完整的JSON请求,而不必担心它们的长度)
  • 缺点 *:这并不直观,您不能一次POST或PUT实体的所有变量,而且此地址/users/4/carPhoto更可以视为一个集合(REST API的标准用例如下所示)。(并且不想)GET/PUT实体的每个变量,例如users/4/name。您可以在users/4处使用GET获取name并使用PUT更改它。如果id后面有内容,则通常是另一个集合,如users/4/review
    3.使用Base64

将其作为JSON发送,但使用Base64编码文件。

  • good*:与第一个解决方案相同,它是尽可能RESTful的服务。
  • 缺点 *:同样,测试和调试要糟糕得多(主体可以有兆字节的数据),大小和处理时间都增加了-客户端和服务器

我真的很想使用第二种解决方案,但它也有它的缺点......谁能给予我一个更好的见解“什么是最好的”解决方案?
我的目标是让REST式服务包含尽可能多的标准,同时我希望尽可能地保持它的简单。

nbysray5

nbysray51#

OP here(我是在两年后回答这个问题的,丹尼尔Cerecedo发的帖子一次也不错,但是Web服务发展得非常快)
经过三年的全职软件开发(同时关注软件架构、项目管理和微服务架构),我肯定会选择第二种方法(但只有一个通用端点)作为最佳方法。

如果您有一个特殊的图像端点,它将为您提供更强大的处理这些图像的能力。
我们为移动的应用程序(iOS/Android)和前端(使用React)提供相同的REST API(Node.js)。现在是2017年,因此您不希望在本地存储图像,您希望将其上传到一些云存储(Google云、s3、cloudinary等),因此您希望对它们进行一些常规处理。
我们的典型流程是,只要你选择一个图像,它开始在后台上传(通常POST在/images端点),上传后返回你的ID。这真的是用户友好的,因为用户选择一个图像,然后通常继续一些其他字段(即地址,姓名,...),因此当他点击“发送”按钮时,图像通常已经上传。他不会等待并看着屏幕上说“上传..."。
获取图像也是如此,特别是由于移动的和有限的移动数据,你不想发送原始图像,你想发送调整大小的图像,所以它们不会占用那么多带宽(为了让你的移动的应用更快,你通常根本不想调整它的大小,你想让图像完美地融入你的视图)。好的应用程序使用类似cloudinary的东西(或者我们确实有自己的图像服务器来调整大小)。
此外,如果数据不是私有的,那么你只需将URL发送回app/frontend,它就可以直接从云存储中下载数据,这可以为你的服务器节省大量的带宽和处理时间。在我们的大型应用中,每个月都有大量的TB下载,你不想在每个REST API服务器上直接处理这些数据。它专注于CRUD操作。你想在一个地方处理它(我们的图像服务器,它有缓存等),或者让云服务处理所有的事情。
2023年小型更新:如果可能的话,但CDN在图片前面,它通常会为您节省很多钱,使图片更可用(iidoEe.没有问题时,高峰发生)。
缺点:唯一的“缺点”,你应该想到的是“未分配的图像”。用户选择图像,并继续填写其他字段,但他说“不”,并关闭应用程序或标签,但同时你成功上传了图像。这意味着你上传了一个图像,没有分配到任何地方。
有几种方法来处理这个问题。最简单的一个是“我不在乎”,这是一个相关的,如果这不是经常发生,或者你甚至希望存储用户发送给你的每一张图片(无论出于什么原因),你不想删除。
另一个也很容易-你有CRON和iidoee.每周,你删除所有未分配的图像超过一周。

dba5bblo

dba5bblo2#

需要做出几个决定

  • 第一条关于资源路径
  • 将图像建模为独立的资源:
  • 嵌套在用户(/user/:id/image)中:用户和图像之间的关系是隐含的
  • 在根路径(/image)中:
  • 客户端负责建立图像和用户之间的关系,或者;
  • 如果为用于创建映像的POST请求提供了安全上下文,则服务器可以隐式地在经过身份验证的用户和映像之间建立关系。
  • 将图像作为用户的一部分嵌入
  • 第二个决策是关于如何表示图像资源
  • 作为Base 64编码的JSON有效负载
  • 作为多部分有效负载
    这将是我的决策跟踪:
  • 我通常更喜欢设计而不是性能,除非有很强的理由。它使系统更易于维护,也更容易被集成商理解。
  • 所以我的第一个想法是使用Base64表示图像资源,因为它允许您将所有内容都保存为JSON。如果选择此选项,您可以根据需要对资源路径进行建模。
  • 如果用户和图像之间的关系是1:1,我倾向于将图像建模为属性,特别是在两个数据集同时更新的情况下。在任何其他情况下,您可以自由选择将图像建模为属性,通过PUT或PATCH更新它,或作为单独的资源。
  • 如果您选择multipart payload,我会觉得必须将图像建模为自己的资源,以便其他资源(在我们的示例中是用户资源)不会受到使用图像的二进制表示的决定的影响。

接下来的问题是:选择base64与multipart是否会对性能产生影响?.我们可能认为以multipart格式交换数据会更高效。但this article显示了两种表示在大小方面的差异非常小。
我的选择Base64:

  • 一致的设计决策
  • 性能影响可忽略不计
  • 由于浏览器理解数据URI(base64编码图像),因此如果客户端是浏览器,则无需转换这些URI
  • 我不会投票决定是否将其作为属性或独立资源,这取决于您的问题域(我不知道)和您的个人偏好。
shstlldc

shstlldc3#

第二个解决方案可能是最正确的,应该按照预期的方式使用HTTP规范和mimetype,并通过multipart/form-data上传文件,至于处理关系,我将使用以下过程(请记住,我对您的假设或系统设计一无所知):

  1. POST/users以创建用户实体。
  2. POST将图像转换为/images,确保返回一个Location标头,可以根据HTTP规范在该标头中检索图像。
  3. PATCH/users/carPhoto,并将步骤2的Location标题中给出的照片ID分配给它。
nfeuvbwi

nfeuvbwi4#

没有简单的解决办法,每种方法都有其优缺点,但最经典的方法是使用第一种选择:multipart/form-data。正如W3推荐指南所述
内容类型“multipart/form-data”应该用于提交包含文件、非ASCII数据和二进制数据的表单。
实际上,我们并没有发送表单,但隐含的原则仍然适用。使用base64作为二进制表示是不正确的,因为您使用了不正确的工具来实现您的目标,另一方面,第二种方法是强迫你的API客户端做更多的工作来使用你的API服务。2你应该在服务器端做一些艰苦的工作来提供一个易于使用的API。第一个选项不容易调试,但是当您调试它时,它可能永远不会改变。
在使用multipart/form-data时,你会被REST/http的哲学所束缚,你可以查看类似问题here的答案。
另一种选择是混合使用这两种方法,您可以使用multipart/form-data,但不是单独发送每个值,而是发送一个名为payload的值,其中包含json payload(我使用ASP.NET WebAPI 2尝试过这种方法,效果很好)。

相关问题