我们正在用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式服务包含尽可能多的标准,同时我希望尽可能地保持它的简单。
4条答案
按热度按时间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.每周,你删除所有未分配的图像超过一周。
dba5bblo2#
需要做出几个决定:
这将是我的决策跟踪:
接下来的问题是:选择base64与multipart是否会对性能产生影响?.我们可能认为以multipart格式交换数据会更高效。但this article显示了两种表示在大小方面的差异非常小。
我的选择Base64:
shstlldc3#
第二个解决方案可能是最正确的,应该按照预期的方式使用HTTP规范和mimetype,并通过
multipart/form-data
上传文件,至于处理关系,我将使用以下过程(请记住,我对您的假设或系统设计一无所知):POST
到/users
以创建用户实体。POST
将图像转换为/images
,确保返回一个Location
标头,可以根据HTTP规范在该标头中检索图像。PATCH
到/users/carPhoto
,并将步骤2的Location
标题中给出的照片ID分配给它。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尝试过这种方法,效果很好)。