Wysyłanie pliku i powiązanych danych do RESTful WebService najlepiej jako JSON
To pewnie będzie głupie pytanie, ale mam jedną z tych nocy. W aplikacji rozwijam RESTful API i chcemy, aby Klient wysyłał dane jako JSON. Część tej aplikacji wymaga od klienta przesłania pliku (Zwykle obrazu), a także informacji o obrazie.
Ciężko mi namierzyć, jak to się dzieje w jednym zapytaniu. Czy jest możliwe, aby Base64 dane Pliku w łańcuchu JSON? Czy będę musiał wykonać 2 posty do serwer? Czy nie powinienem używać do tego JSON?
Na marginesie, używamy Grails na zapleczu i te usługi są dostępne dla natywnych klientów mobilnych (iPhone, Android, itp.), Jeśli coś z tego robi różnicę.
11 answers
Zadałem podobne pytanie tutaj:
Jak przesłać plik z metadanymi za pomocą usługi internetowej REST?
W zasadzie masz trzy opcje:
- Base64 koduje plik, kosztem zwiększenia rozmiaru danych o około 33%.
- Wyślij najpierw plik w poście
multipart/form-data
i zwróć ID klientowi. Następnie klient wysyła metadane z identyfikatorem, a serwer ponownie kojarzy plik i metadane. - najpierw Wyślij metadane, a zwróć klientowi identyfikator. Następnie klient wysyła plik z identyfikatorem, a serwer ponownie kojarzy plik i metadane.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:25
Możesz przesłać plik i dane w jednym żądaniu za pomocą multipart/form-data content Type:
W wielu aplikacjach możliwe jest przedstawienie użytkownika z formularz. Użytkownik wypełni formularz, w tym informację, że jest wpisywane, generowane przez wejście użytkownika lub dołączane z plików, które użytkownik wybrał. Po wypełnieniu formularza dane z formularz jest wysyłany od użytkownika do otrzymującej aplikacji.
Definicja MultiPart/formularz-Dane pochodzą z jednego z tych aplikacje...
Z http://www.faqs.org/rfcs/rfc2388.html :
"multipart / form-data" zawiera szereg części. Każda część jest oczekuje się, że będzie zawierał nagłówek Content-disposition [RFC 2183], gdzie typem dyspozycyjnym jest "form-data" i gdzie dyspozycja zawiera (dodatkowy) parametr "name", gdzie wartość tego parametr jest oryginalną nazwą pola w formularzu. Na przykład, a część może zawierać nagłówek:
Content-Disposition: form-data; name= "użytkownik"
Z wartością odpowiadającą wpisowi pola "user".
W każdej sekcji między granicami można umieścić informacje o pliku lub informacje o polach. Z powodzeniem wdrożyłem usługę RESTful, która wymagała od użytkownika podania zarówno danych, jak i formularza, a multipart / form-data działało idealnie. Usługa została zbudowana przy użyciu Java / Spring, a Klient korzystał z C#, więc niestety nie mam żadnych przykładów Grails, aby podać jak skonfigurować usługę. W tym przypadku nie musisz używać JSON, ponieważ każda sekcja "form-data" zapewnia miejsce do podania nazwy parametru i jego wartości.
Dobrą rzeczą w używaniu multipart / form-data jest to, że używasz nagłówków zdefiniowanych przez HTTP, więc pozostajesz przy filozofii korzystania z istniejących narzędzi HTTP do tworzenia usługi.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-03 02:49:09
Wiem, że ten wątek jest dość stary, jednak brakuje mi tutaj jednej opcji. Jeśli masz metadane (w dowolnym formacie), które chcesz wysłać wraz z danymi do przesłania, możesz złożyć jedno żądanie multipart/related
.
Możesz sprawdzić specyfikację RFC 2387 , aby uzyskać więcej szczegółowych informacji.Multipart/Related media type jest przeznaczony dla obiektów złożonych składających się z kilku powiązanych ze sobą części ciała.
W zasadzie każda część takiego żądania może mieć zawartość o innym typie i wszystkie części są w jakiś sposób powiązane(np. obraz i metadane it). Części są identyfikowane za pomocą ciągu granicznego, a po końcowym ciągu granicznym następują dwa myślniki.
Przykład:
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--
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-23 15:03:28
Wiem, że to pytanie jest stare, ale w ostatnich dniach przeszukałem całą sieć, aby rozwiązać to samo pytanie. Mam grails REST webservices i klienta iPhone ' a, który wysyła zdjęcia, tytuł i opis.
Nie wiem, czy moje podejście jest najlepsze, ale jest takie łatwe i proste.Robię zdjęcie używając UIImagePickerController i wysyłam do serwera NSData używając znaczników nagłówka żądania, aby wysłać dane Zdjęcia.
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];
Po stronie serwera otrzymuję zdjęcie użycie kodu:
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")
}
Nie wiem, czy mam problemy w przyszłości, ale teraz działa dobrze w środowisku produkcyjnym.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-28 13:38:06
Oto moje API podejścia ( używam przykładu) - jak widać, nie używam żadnego file_id (identyicator przesłanego pliku na serwerze) w API:
1.Tworzenie obiektu 'photo' na serwerze:
POST: /projects/{project_id}/photos
params in: {name:some_schema.jpg, comment:blah}
return: photo_id
2.Prześlij plik (zauważ, że 'plik' jest w liczbie pojedynczej, ponieważ jest tylko jeden na zdjęcie):
POST: /projects/{project_id}/photos/{photo_id}/file
params in: file to upload
return: -
I wtedy na przykład:
3.Przeczytaj listę zdjęć
GET: /projects/{project_id}/photos
params in: -
return: array of objects: [ photo, photo, photo, ... ]
4.Przeczytaj szczegóły zdjęcia
GET: /projects/{project_id}/photos/{photo_id}
params in: -
return: photo = { id: 666, name:'some_schema.jpg', comment:'blah'}
5.Read photo file
GET: /projects/{project_id}/photos/{photo_id}/file
params in: -
return: file content
Więc wniosek jest taki, że najpierw ty Utwórz obiekt (zdjęcie) pocztą, a następnie wyślij żądanie secod z plikiem(ponownie POST).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-05 15:20:53
Ponieważ jedynym brakującym przykładem jest Przykład Androida , dodam go. Ta technika używa niestandardowego Asynktasku, który powinien być zadeklarowany wewnątrz klasy 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);
}
}
Więc, gdy chcesz wgrać swój plik po prostu zadzwoń:
new UploadFile().execute();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-10 09:45:23
Obiekty FormData: Przesyłanie Plików Za Pomocą Ajax
XMLHttpRequest Poziom 2 dodaje obsługę nowego interfejsu FormData. Obiekty FormData umożliwiają łatwe konstruowanie zestawu par klucz / wartość reprezentujących pola formularza i ich wartości, które można następnie łatwo przesłać za pomocą metody 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);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-17 13:29:47
@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
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-30 09:47:22
Chciałem wysłać kilka ciągów do serwera backend. Nie używałem json z multipart, użyłem request params.
@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 wyglądałby jak
http://localhost:8080/file/upload?uuid=46f073d0&type=PASSPORT
Przekazuję dwa paramy (uuid I type) wraz z przesłaniem pliku. Mam nadzieję, że pomoże to tym, którzy nie mają złożonych danych json do wysłania.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-18 18:22:30
Upewnij się, że masz następujący import. Ofcourse other standard imports
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
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-10 14:54:12
Jeśli tworzysz serwer rest możesz to zrobić
- niech klient wystawi plik przez HTTP
- klient może następnie wysłać adres url z danymi json, np. plik obrazu
{"file_url":"http://cockwombles.com/blah.jpg"}
- serwer może następnie pobrać plik.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-17 14:20:07