json - servidor - rest api tutorial español
Publicación de un archivo y datos asociados a un servicio web RESTful preferiblemente como JSON (11)
Aquí está mi API de enfoque (yo uso ejemplo) - como puede ver, usted no usa ningún ID de archivo (identificador de archivo cargado en el servidor) en la API:
1.Crear el objeto ''foto'' en el servidor:
POST: /projects/{project_id}/photos
params in: {name:some_schema.jpg, comment:blah}
return: photo_id
2. Cargue el archivo (tenga en cuenta que ''archivo'' está en forma singular porque es solo uno por foto):
POST: /projects/{project_id}/photos/{photo_id}/file
params in: file to upload
return: -
Y luego, por ejemplo:
3.Leer lista de fotos
GET: /projects/{project_id}/photos
params in: -
return: array of objects: [ photo, photo, photo, ... ]
4.Lea algunos detalles de la foto
GET: /projects/{project_id}/photos/{photo_id}
params in: -
return: photo = { id: 666, name:''some_schema.jpg'', comment:''blah''}
5. leer el archivo de fotos
GET: /projects/{project_id}/photos/{photo_id}/file
params in: -
return: file content
Entonces, la conclusión es que primero creas un objeto (foto) por POST y luego envías una solicitud secod con el archivo (otra vez POST).
Probablemente esta sea una pregunta estúpida, pero tengo una de esas noches. En una aplicación estoy desarrollando una API REST y queremos que el cliente envíe datos como JSON. Parte de esta aplicación requiere que el cliente cargue un archivo (generalmente una imagen) así como información sobre la imagen.
Estoy teniendo dificultades para rastrear cómo sucede esto en una sola solicitud. ¿Es posible basar los datos del archivo en una cadena JSON? ¿Tendré que realizar 2 publicaciones al servidor? ¿No debería usar JSON para esto?
Como nota al margen, estamos usando Grails en el backend y estos servicios son accedidos por clientes móviles nativos (iPhone, Android, etc.), si alguno de ellos hace una diferencia.
Como el único ejemplo que falta es el ejemplo de ANDROID , lo agregaré. Esta técnica utiliza una AsyncTask personalizada que debe declararse dentro de su clase de actividad.
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);
}
}
Entonces, cuando quieras subir tu archivo solo llama:
new UploadFile().execute();
Hice una pregunta similar aquí:
¿Cómo subo un archivo con metadatos usando un servicio web REST?
Básicamente tienes tres opciones:
- Base64 codifica el archivo, a costa de aumentar el tamaño de los datos en aproximadamente un 33%.
- Primero envíe el archivo en un POST
multipart/form-data
y devuelva una ID al cliente. Luego, el cliente envía los metadatos con el ID y el servidor vuelve a asociar el archivo y los metadatos. - Primero envíe los metadatos y devuelva una ID al cliente. El cliente luego envía el archivo con la ID y el servidor vuelve a asociar el archivo y los metadatos.
Objetos de FormData: carga archivos usando Ajax
XMLHttpRequest Level 2 agrega soporte para la nueva interfaz FormData. Los objetos FormData proporcionan una manera de construir fácilmente un conjunto de pares clave / valor que representan campos de formulario y sus valores, que luego pueden enviarse fácilmente usando el método de envío () XMLHttpRequest.
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);
}
Por favor, asegúrese de que tiene la siguiente importación. Por supuesto, otras importaciones estándar
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
}
Puede enviar el archivo y los datos en una solicitud utilizando el tipo de contenido multipart/form-data :
En muchas aplicaciones, es posible que a un usuario se le presente un formulario. El usuario completará el formulario, incluida la información que se escribe, se genera mediante la entrada del usuario o se incluye a partir de los archivos que el usuario ha seleccionado. Cuando se completa el formulario, los datos del formulario se envían desde el usuario a la aplicación receptora.
La definición de MultiPart / Form-Data se deriva de una de esas aplicaciones ...
Desde http://www.faqs.org/rfcs/rfc2388.html :
"multipart / form-data" contiene una serie de partes. Se espera que cada parte contenga un encabezado de disposición de contenido [RFC 2183] donde el tipo de disposición es "form-data", y donde la disposición contiene un parámetro (adicional) de "nombre", donde el valor de ese parámetro es el original Nombre del campo en el formulario. Por ejemplo, una parte puede contener un encabezado:
Disposición de contenido: datos de formulario; nombre = "usuario"
con el valor correspondiente a la entrada del campo "usuario".
Puede incluir información de archivo o información de campo dentro de cada sección entre límites. He implementado con éxito un servicio RESTful que requería que el usuario enviara tanto datos como un formulario, y multiparte / datos de formulario funcionaron perfectamente. El servicio se creó utilizando Java / Spring, y el cliente usaba C #, por lo que, lamentablemente, no tengo ningún ejemplo de Grails para proporcionarle información sobre cómo configurar el servicio. No necesita usar JSON en este caso, ya que cada sección de "datos de formulario" le proporciona un lugar para especificar el nombre del parámetro y su valor.
Lo bueno de usar multipart / form-data es que estás usando encabezados definidos por HTTP, por lo que te quedas con la filosofía REST de usar las herramientas HTTP existentes para crear tu servicio.
Quería enviar algunas cadenas al servidor backend. No usé json con multiparte, he usado los parámetros de solicitud.
@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 se vería como
http://localhost:8080/file/upload?uuid=46f073d0&type=PASSPORT
Estoy pasando dos parámetros (uuid y tipo) junto con la carga de archivos. Espero que esto ayude a quienes no tienen los datos complejos de json para enviar.
Sé que esta pregunta es antigua, pero en los últimos días había buscado en toda la web para resolver esta misma pregunta. Tengo servicios web REST de Grails y iPhone Client que envían imágenes, títulos y descripciones.
No sé si mi enfoque es el mejor, pero es tan fácil y simple.
Tomo una imagen utilizando el UIImagePickerController y envío al servidor los datos NS utilizando las etiquetas de encabezado de solicitud para enviar los datos de la imagen.
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];
En el lado del servidor, recibo la foto usando el código:
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")
}
No sé si tengo problemas en el futuro, pero ahora funciona bien en el entorno de producción.
Sé que este hilo es bastante antiguo, sin embargo, me falta una opción. Si tiene metadatos (en cualquier formato) que desea enviar junto con los datos para cargar, puede realizar una única solicitud multipart/related
.
El tipo de medio Multipart / Related está destinado a objetos compuestos que constan de varias partes del cuerpo interrelacionadas.
Puede consultar la especificación RFC 2387 para obtener más detalles en profundidad.
Básicamente, cada parte de dicha solicitud puede tener contenido de diferente tipo y todas las partes están relacionadas de alguna manera (por ejemplo, una imagen y sus metadatos). Las partes se identifican por una cadena de límite, y la cadena de límite final es seguida por dos guiones.
Ejemplo:
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--
Si está desarrollando un servidor de descanso, puede hacer esto.
- Haga que el cliente exponga el archivo a través de HTTP.
- El cliente puede enviar la url con sus datos json, por ejemplo, un archivo de imagen
{"file_url":"http://cockwombles.com/blah.jpg"}
- El servidor puede descargar el archivo.
@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
}