Публикация файла и связанных с ним данных в веб-сервис RESTful предпочтительно как JSON
Это, вероятно, будет глупый вопрос, но у меня есть одна из тех ночей. В приложении я разрабатываю RESTful API, и мы хотим, чтобы клиент отправлял данные как JSON. Часть этого приложения требует, чтобы клиент загрузил файл (обычно изображение), а также информацию об изображении.
Мне трудно отслеживать, как это происходит в одном запросе. Можно ли Base64 данные файла в строку JSON? Мне нужно будет выполнить 2 сообщения на сервер? Я не должен использовать JSON для этого?
в качестве примечания мы используем Grails на бэкэнде, и эти услуги доступны для собственных мобильных клиентов (iPhone, Android и т. д.), Если это имеет значение.
11 ответов:
Я задал аналогичный вопрос здесь:
как загрузить файл с метаданными с помощью веб-службы REST?
у вас в основном есть три варианта:
- Base64 кодирует файл, за счет увеличения размера данных примерно на 33%.
- отправить файл в
multipart/form-data
сообщение и возврат идентификатора клиенту. Затем клиент отправляет метаданные с идентификатором, а сервер повторно связывает файл и идентификатор. метаданные.- сначала отправьте метаданные и верните клиенту идентификатор. Затем клиент отправляет файл с идентификатором, а сервер повторно связывает файл и метаданные.
вы можете отправить файл и данные в одном запросе с помощью multipart / form-data тип содержимого:
во многих приложениях пользователь может быть представлен с помощью форма. Пользователь заполнит форму, включая информацию о том, что типизируется, генерируется пользовательским вводом или включается из файлов, которые пользователь выбрал. Когда форма заполнена, данные из форма отправляется от пользователя в принимающее приложение.
определение MultiPart / Form-Data выводится из одного из них приложения...
от http://www.faqs.org/rfcs/rfc2388.html:
"multipart / form-data" содержит ряд деталей. Каждая часть ожидается, что он будет содержать заголовок content-disposition [RFC 2183], где тип распоряжения - "форма-данные", и где распоряжение содержит (дополнительный) параметр "name" , где значение этого параметр исходное имя поля в форме. Например, деталь может содержать заголовок:
Content-Disposition: form-data; name= "user"
со значением, соответствующим вводу поля "пользователь".
вы можете включить информацию о файле или информацию о поле в каждом разделе между границами. Я успешно реализовал службу RESTful, которая требовала от пользователя представить как данные, так и форму, и multipart/form-data работали отлично. Этот служба была построена с использованием Java / Spring, а клиент использовал C#, поэтому, к сожалению, у меня нет примеров Grails, чтобы дать вам информацию о том, как настроить службу. В этом случае вам не нужно использовать JSON, так как каждый раздел "форма-данные" предоставляет вам место для указания имени параметра и его значения.
хорошая вещь об использовании multipart / form-data заключается в том, что вы используете http-определенные заголовки, поэтому вы придерживаетесь философии REST использования существующих инструментов HTTP для создайте свой сервис.
Я знаю, что эта нить довольно старая, однако, мне не хватает здесь одного варианта. Если у вас есть метаданные (в любом формате), который вы хотите отправить вместе с данными для загрузки, вы можете сделать один
multipart/related
запрос.в multipart/Тип издания предназначен для составных объектов, состоящих из нескольких взаимосвязанных частей тела.
Вы можете проверить 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 и клиент 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") }
Я не знаю, если у меня возникнут проблемы в будущем, но сейчас работает нормально, в рабочей среде.
вот мой подход API (я использую пример) - как вы можете видеть, я не использую никакой file_id (загруженный файл identyicator на сервере) в API:
1.Создать объект "фото" на сервере:
POST: /projects/{project_id}/photos params in: {name:some_schema.jpg, comment:blah} return: photo_id
2.Загрузите файл (обратите внимание, что "файл" находится в единственном числе, потому что это только один на фотографию):
POST: /projects/{project_id}/photos/{photo_id}/file params in: file to upload return: -
и тогда, например:
3.Читать список фотографий
GET: /projects/{project_id}/photos params in: - return: array of objects: [ photo, photo, photo, ... ]
4.Читайте некоторые детали фото
GET: /projects/{project_id}/photos/{photo_id} params in: - return: photo = { id: 666, name:'some_schema.jpg', comment:'blah'}
5.Читать фото файл
GET: /projects/{project_id}/photos/{photo_id}/file params in: - return: file content
Итак, вывод заключается в том, что сначала вы создаете объект (фото) по почте, а затем отправляете запрос secod с файлом (снова POST).
поскольку единственным недостающим примером является пример 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();
Объекты 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); }
@RequestMapping(value = "/uploadImageJson", method = RequestMethod.POST) public @ResponseBody Object jsongStrImage(@RequestParam(value="image") MultipartFile image, @RequestParam String jsonStr) { -- use com.fasterxml.jackson.databind.ObjectMapper convert Json String to Object }
Я хотел отправить некоторые строки на backend сервер. Я не использовал 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 и тип) вместе с загрузкой файла. Надеюсь, это поможет тем, у кого нет сложных данных json для отправки.
пожалуйста, убедитесь, что у вас после импорта. Конечно, другие стандартные импорт
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 }
Если вы разрабатываете сервер rest, вы можете сделать это
- пусть клиент предоставляет файл через HTTP
- затем клиент может отправить url-адрес с вашими данными json, например, файл изображения
{"file_url":"http://cockwombles.com/blah.jpg"}
- затем сервер может загрузить файл.
Comments