在一个应用程序中,我正在开发RESTful API,我们希望客户端以JSON形式发送数据。这个应用程序的一部分要求客户端上传一个文件(通常是一个图像)以及关于图像的信息。

我很难在一个请求中找到这种情况。是否可以将文件数据Base64转换为JSON字符串?我是否需要向服务器发送2次帖子?我不应该使用JSON吗?

顺便说一句,我们在后端使用Grails,这些服务是由本地移动客户端(iPhone、Android等)访问的,如果有什么不同的话。


当前回答

FormData对象:使用Ajax上传文件

XMLHttpRequest Level 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);
}

https://developer.mozilla.org/en-US/docs/Web/API/FormData

其他回答

由于唯一缺少的例子是ANDROID的例子,我将添加它。 该技术使用一个自定义AsyncTask,应该在Activity类中声明。

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();

我知道这个线程是相当旧的,但是,我在这里错过了一个选项。如果您有元数据(任何格式),希望将其与要上传的数据一起发送,则可以发出单个多部分/相关请求。

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--

我想发送一些字符串到后端服务器。我没有使用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)

Url看起来像这样

http://localhost:8080/file/upload?uuid=46f073d0&type=PASSPORT

我在文件上传时传递了两个参数(uuid和type)。 希望这将帮助谁没有复杂的json数据发送。

您可以尝试使用https://square.github.io/okhttp/ library。 你可以设置请求主体为multipart,然后分别添加文件和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());

我知道这个问题很老了,但在过去的几天里,我搜索了整个网络来解决这个问题。我有grails REST web服务和iPhone客户端发送图片,标题和描述。

我不知道我的方法是不是最好的,但是很简单。

我使用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")

}

我不知道将来是否会有问题,但现在在生产环境中工作得很好。