这可能是一个愚蠢的问题,但我有一个夜晚。在一个应用程序中,我正在开发RESTful API,我们希望客户端将数据作为JSON发送。此应用程序的一部分要求客户端上载文件(通常是图像)以及有关该图像的信息。
我很难跟踪单个请求中的情况。是否可以将文件数据Base64转换为JSON字符串?我需要在服务器上执行2个帖子吗?我不应该为此使用JSON吗?
附带说明一下,我们在后端使用Grails,并且本机移动客户端(iPhone,Android等)可以访问这些服务(如果有区别的话)。
这可能是一个愚蠢的问题,但我有一个夜晚。在一个应用程序中,我正在开发RESTful API,我们希望客户端将数据作为JSON发送。此应用程序的一部分要求客户端上载文件(通常是图像)以及有关该图像的信息。
我很难跟踪单个请求中的情况。是否可以将文件数据Base64转换为JSON字符串?我需要在服务器上执行2个帖子吗?我不应该为此使用JSON吗?
附带说明一下,我们在后端使用Grails,并且本机移动客户端(iPhone,Android等)可以访问这些服务(如果有区别的话)。
Answers:
我在这里问了类似的问题:
您基本上有三个选择:
multipart/form-dataPOST中发送文件,然后将ID返回给客户端。然后,客户端发送带有ID的元数据,然后服务器将文件和元数据重新关联。您可以使用multipart / form-data 内容类型在一个请求中发送文件和数据:
在许多应用中,有可能向用户呈现表格。用户将填写该表单,包括键入的信息,通过用户输入生成的信息或包含在用户选择的文件中的信息。填写表单后,表单中的数据将从用户发送到接收应用程序。
MultiPart / Form-Data的定义源自这些应用程序之一。
从http://www.faqs.org/rfcs/rfc2388.html:
“ multipart / form-data”包含一系列部分。每个部分均应包含一个内容处理标头[RFC 2183],其中处理类型为“ form-data”,并且该处理包含一个(附加)参数“ name”,其中该参数的值为原始值表单中的字段名称。例如,零件可能包含标题:
内容处置:表单数据;名称=“用户”
其值与“用户”字段的输入相对应。
您可以在边界之间的每个部分中包括文件信息或字段信息。我已经成功实现了RESTful服务,该服务要求用户提交数据和表单,并且multipart / form-data可以完美地工作。该服务是使用Java / Spring构建的,而客户端使用的是C#,因此,不幸的是,我没有任何Grails示例可以向您提供有关如何设置服务的信息。在这种情况下,您不需要使用JSON,因为每个“表单数据”部分都为您提供了一个位置,用于指定参数的名称及其值。
使用multipart / form-data的好处是,您使用的是HTTP定义的标头,因此您坚持使用现有HTTP工具创建服务的REST理念。
我知道这个线程已经很老了,但是,我这里缺少一个选项。如果您具有要发送的元数据(任何格式)以及要上传的数据,则可以发出单个multipart/related请求。
Multipart / Related媒体类型适用于由几个相互关联的身体部位组成的复合对象。
您可以查看RFC 2387规范以了解更多详细信息。
基本上,此类请求的每个部分都可以具有不同类型的内容,并且所有部分都以某种方式相关(例如,图像及其元数据)。零件由边界字符串标识,最后的边界字符串后跟两个连字符。
例:
POST /upload HTTP/1.1
Host: www.hostname.com
Content-Type: multipart/related; boundary=xyz
Content-Length: [actual-content-length]
--xyz
Content-Type: application/json; charset=UTF-8
{
    "name": "Sample image",
    "desc": "...",
    ...
}
--xyz
Content-Type: image/jpeg
[image data]
[image data]
[image data]
...
--foo_bar_baz--
              我知道这个问题很旧,但是在最近的几天里,我搜索了整个网络以解决这个问题。我有grails REST Web服务和iPhone Client,它们发送图片,标题和描述。
我不知道我的方法是否是最好的,但如此简单。
我使用UIImagePickerController拍摄图片,然后使用请求的标头标签发送NSData到服务器,以发送图片的数据。
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"myServerAddress"]];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:UIImageJPEGRepresentation(picture, 0.5)];
[request setValue:@"image/jpeg" forHTTPHeaderField:@"Content-Type"];
[request setValue:@"myPhotoTitle" forHTTPHeaderField:@"Photo-Title"];
[request setValue:@"myPhotoDescription" forHTTPHeaderField:@"Photo-Description"];
NSURLResponse *response;
NSError *error;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
在服务器端,我使用以下代码接收照片:
InputStream is = request.inputStream
def receivedPhotoFile = (IOUtils.toByteArray(is))
def photo = new Photo()
photo.photoFile = receivedPhotoFile //photoFile is a transient attribute
photo.title = request.getHeader("Photo-Title")
photo.description = request.getHeader("Photo-Description")
photo.imageURL = "temp"    
if (photo.save()) {    
    File saveLocation = grailsAttributes.getApplicationContext().getResource(File.separator + "images").getFile()
    saveLocation.mkdirs()
    File tempFile = File.createTempFile("photo", ".jpg", saveLocation)
    photo.imageURL = saveLocation.getName() + "/" + tempFile.getName()
    tempFile.append(photo.photoFile);
} else {
    println("Error")
}
我不知道将来是否会遇到问题,但现在在生产环境中可以正常工作。
这是我的方法API(我使用的示例)-如您所见,您file_id在API中不使用任何(将文件标识符上传到服务器):
photo在服务器上创建对象:
POST: /projects/{project_id}/photos   
body: { name: "some_schema.jpg", comment: "blah"}
response: photo_id上载文件(请注意file采用单数形式,因为每张照片只有一个):
POST: /projects/{project_id}/photos/{photo_id}/file
body: file to upload
response: -然后例如:
阅读照片列表
GET: /projects/{project_id}/photos
response: [ photo, photo, photo, ... ] (array of objects)阅读一些照片细节
GET: /projects/{project_id}/photos/{photo_id}
response: { id: 666, name: 'some_schema.jpg', comment:'blah'} (photo object)读取照片文件
GET: /projects/{project_id}/photos/{photo_id}/file
response: file content因此得出的结论是,首先通过POST创建对象(照片),然后发送带有文件的第二个请求(同样是POST)。
FormData对象:使用Ajax上传文件
XMLHttpRequest级别2增加了对新FormData接口的支持。FormData对象提供了一种轻松构造一组代表表单字段及其值的键/值对的方法,然后可以使用XMLHttpRequest send()方法轻松发送该键/值对。
function AjaxFileUpload() {
    var file = document.getElementById("files");
    //var file = fileInput;
    var fd = new FormData();
    fd.append("imageFileData", file);
    var xhr = new XMLHttpRequest();
    xhr.open("POST", '/ws/fileUpload.do');
    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
             alert('success');
        }
        else if (uploadResult == 'success')
             alert('error');
    };
    xhr.send(fd);
}
              由于唯一缺少的示例是ANDROID示例,因此我将其添加。该技术使用了应在Activity类中声明的自定义AsyncTask。
private class UploadFile extends AsyncTask<Void, Integer, String> {
    @Override
    protected void onPreExecute() {
        // set a status bar or show a dialog to the user here
        super.onPreExecute();
    }
    @Override
    protected void onProgressUpdate(Integer... progress) {
        // progress[0] is the current status (e.g. 10%)
        // here you can update the user interface with the current status
    }
    @Override
    protected String doInBackground(Void... params) {
        return uploadFile();
    }
    private String uploadFile() {
        String responseString = null;
        HttpClient httpClient = new DefaultHttpClient();
        HttpPost httpPost = new HttpPost("http://example.com/upload-file");
        try {
            AndroidMultiPartEntity ampEntity = new AndroidMultiPartEntity(
                new ProgressListener() {
                    @Override
                        public void transferred(long num) {
                            // this trigger the progressUpdate event
                            publishProgress((int) ((num / (float) totalSize) * 100));
                        }
            });
            File myFile = new File("/my/image/path/example.jpg");
            ampEntity.addPart("fileFieldName", new FileBody(myFile));
            totalSize = ampEntity.getContentLength();
            httpPost.setEntity(ampEntity);
            // Making server call
            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                responseString = EntityUtils.toString(httpEntity);
            } else {
                responseString = "Error, http status: "
                        + statusCode;
            }
        } catch (Exception e) {
            responseString = e.getMessage();
        }
        return responseString;
    }
    @Override
    protected void onPostExecute(String result) {
        // if you want update the user interface with upload result
        super.onPostExecute(result);
    }
}
因此,当您要上传文件时,只需调用:
new UploadFile().execute();
              我想发送一些字符串到后端服务器。我没有将json与multipart一起使用,而是使用了请求参数。
@RequestMapping(value = "/upload", method = RequestMethod.POST)
public void uploadFile(HttpServletRequest request,
        HttpServletResponse response, @RequestParam("uuid") String uuid,
        @RequestParam("type") DocType type,
        @RequestParam("file") MultipartFile uploadfile)
网址看起来像
http://localhost:8080/file/upload?uuid=46f073d0&type=PASSPORT
我正在传递两个参数(uuid和type)以及文件上传。希望这对没有复杂json数据发送的人有所帮助。
您可以尝试使用https://square.github.io/okhttp/库。您可以将请求主体设置为多部分,然后分别添加文件和json对象,如下所示:
MultipartBody requestBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("uploadFile", uploadFile.getName(), okhttp3.RequestBody.create(uploadFile, MediaType.parse("image/png")))
                .addFormDataPart("file metadata", json)
                .build();
        Request request = new Request.Builder()
                .url("https://uploadurl.com/uploadFile")
                .post(requestBody)
                .build();
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
            logger.info(response.body().string());
              请确保您具有以下导入。当然其他标准进口
import org.springframework.core.io.FileSystemResource
    void uploadzipFiles(String token) {
        RestBuilder rest = new RestBuilder(connectTimeout:10000, readTimeout:20000)
        def zipFile = new File("testdata.zip")
        def Id = "001G00000"
        MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>()
        form.add("id", id)
        form.add('file',new FileSystemResource(zipFile))
        def urld ='''http://URL''';
        def resp = rest.post(urld) {
            header('X-Auth-Token', clientSecret)
            contentType "multipart/form-data"
            body(form)
        }
        println "resp::"+resp
        println "resp::"+resp.text
        println "resp::"+resp.headers
        println "resp::"+resp.body
        println "resp::"+resp.status
    }
              java.lang.ClassCastException: org.springframework.core.io.FileSystemResource cannot be cast to java.lang.String