我有一个REST Web服务,目前公开此URL:
HTTP://服务器/数据/媒体
用户可以POST
使用以下JSON:
{ "Name": "Test", "Latitude": 12.59817, "Longitude": 52.12873 }
为了创建新的媒体元数据.
现在我需要能够在媒体元数据的同时上传文件.解决这个问题的最佳方法是什么?我可以引入一个名为file
base64 的新属性编码文件,但我想知道是否有更好的方法.
还有multipart/form-data
像HTML表单一样使用,但我正在使用REST Web服务,我想尽可能坚持使用JSON.
我同意格雷格认为两阶段方法是一个合理的解决方案,但我会反过来做.我会做:
POST http://server/data/media body: { "Name": "Test", "Latitude": 12.59817, "Longitude": 52.12873 }
要创建元数据条目并返回响应,例如:
201 Created Location: http://server/data/media/21323 { "Name": "Test", "Latitude": 12.59817, "Longitude": 52.12873, "ContentUrl": "http://server/data/media/21323/content" }
然后,客户端可以使用此ContentUrl并使用文件数据执行PUT.
这种方法的好处在于,当您的服务器开始被大量数据压缩时,您返回的URL可以指向具有更多空间/容量的其他服务器.或者,如果带宽是个问题,您可以实施某种循环方法.
仅仅因为您没有将整个请求主体包装在JSON中,并不意味着用于multipart/form-data
在单个请求中发布JSON和文件(或多个文件)并不是RESTful :
curl -F "metadata=在服务器端(使用Python作为编程通用语):
class AddFileResource(Resource): def render_POST(self, request): metadata = json.loads(request.args['metadata'][0]) file_body = request.args['file'][0] ...要上传多个文件,可以为每个文件使用单独的"表单字段":
curl -F "metadata=...在这种情况下,服务器代码将具有
request.args['file1'][0]
和request.args['file2'][0]
或者为许多人重复使用同一个:
curl -F "metadata=......在这种情况下,它
request.args['files']
只是一个长度为2的列表.或实际上一次将多个文件传递到一个字段:
curl -F "metadata=...在这种情况下
request.args['files']
将是一个包含所有文件的字符串,你必须自己解析 - 不知道如何做,但我确信这并不困难,或者更好地使用以前的方法.
@
和之间的区别<
是@
导致文件作为文件上载<
附加,而将文件的内容作为文本字段附加.PS仅仅因为我
curl
用作生成POST
请求的方式并不意味着无法从Python等编程语言或使用任何足够强大的工具发送完全相同的HTTP请求.
我自己一直想知道这个方法,为什么我还没有看到其他人说出来.我同意,对我来说似乎很完美.
@mjolnic你的评论无关紧要:cURL的例子很好,_examples_; 答案明确指出你可以使用任何东西来发送请求......还有什么阻止你只写`curl -f'metadata = {"foo":"bar"}'`?
我正在使用这种方法,因为接受的答案对我正在开发的应用程序不起作用(该文件在数据之前不存在,并且它增加了不必要的复杂性来处理首先上载数据并且文件永远不会上传的情况) .
3> Greg Hewgill..:解决问题的一种方法是使上传成为两阶段过程.首先,您将使用POST上传文件本身,其中服务器将一些标识符返回给客户端(标识符可能是文件内容的SHA1).然后,第二个请求将元数据与文件数据相关联:
{ "Name": "Test", "Latitude": 12.59817, "Longitude": 52.12873, "ContentID": "7a788f56fa49ae0ba5ebde780efe4d6a89b5db47" }包括编码到JSON请求本身的文件数据base64将使传输的数据量增加33%.根据文件的整体大小,这可能重要也可能不重要.
另一种方法可能是使用原始文件数据的POST,但在HTTP请求标头中包含任何元数据.但是,这有点落在基本的REST操作之外,对某些HTTP客户端库来说可能更尴尬.
4> Greg Biles..:我意识到这是一个非常古老的问题,但希望这会帮助其他人,因为我在这篇文章中寻找同样的事情.我有一个类似的问题,只是我的元数据是Guid和int.解决方案是一样的.您只需在URL中创建所需的元数据即可.
"Controller"类中的POST接受方法:
public TaskPostFile(string name, float latitude, float longitude) { //See http://stackoverflow.com/a/10327789/431906 for how to accept a file return null; } 然后在你注册路由的任何地方,WebApiConfig.Register(HttpConfiguration config)对我来说就是这种情况.
config.Routes.MapHttpRoute( name: "FooController", routeTemplate: "api/{controller}/{name}/{latitude}/{longitude}", defaults: new { } );