FileUpload avec JAX-RS
j'essaie de télécharger des fichiers à partir d'un client JavaScript vers un serveur Java JAX-RS.
j'utilise la fonction REST upload suivante sur mon serveur:
@POST
@Produces('application/json')
UploadDto upload(
@Context HttpServletRequest request,
@QueryParam("cookie") String cookie) {
def contentType
byte [] fileBytes
log.debug "upload - cookie: "+cookie
try{
if (request instanceof MultipartHttpServletRequest) {
log.debug "request instanceof MultipartHttpServletRequest"
MultipartHttpServletRequest myrequest = request
CommonsMultipartFile file = (CommonsMultipartFile) myrequest.getFile('file')
fileBytes = file.bytes
contentType = file.contentType
log.debug ">>>>> upload size of the file in byte: "+ file.size
}
else if (request instanceof SecurityContextHolderAwareRequestWrapper) {
log.debug "request instanceof SecurityContextHolderAwareRequestWrapper"
SecurityContextHolderAwareRequestWrapper myrequest = request
//get uploaded file's inputStream
InputStream inputStream = myrequest.inputStream
fileBytes = IOUtils.toByteArray(inputStream);
contentType = myrequest.getHeader("Content-Type")
log.debug ">>>>> upload size of the file in byte: "+ fileBytes.size()
}
else {
log.error "request is not a MultipartHttpServletRequest or SecurityContextHolderAwareRequestWrapper"
println "request: "+request.class
}
}
catch (IOException e) {
log.error("upload() failed to save file error: ", e)
}
}
du côté client j'envoie le fichier comme suit:
var str2ab_blobreader = function(str, callback) {
var blob;
BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder
|| window.BlobBuilder;
if (typeof (BlobBuilder) !== 'undefined') {
var bb = new BlobBuilder();
bb.append(str);
blob = bb.getBlob();
} else {
blob = new Blob([ str ]);
}
var f = new FileReader();
f.onload = function(e) {
callback(e.target.result)
}
f.readAsArrayBuffer(blob);
}
var fileName = "fileName.jpg";
var contentType = "image/jpeg";
if (file.type.toString().toLowerCase().indexOf("png") > -1) {
fileName = "fileName.png";
contentType = "image/png";
}
var xhrNativeObject = new XMLHttpRequest();
var urlParams = ?test=123;
xhrNativeObject.open("post", url + urlParams, true);
xhrNativeObject.setRequestHeader("Content-Type", contentType);
xhrNativeObject.onload = function(event) {
var targetResponse = event.currentTarget;
if ((targetResponse.readyState == 4)
&& (targetResponse.status == 200)) {
var obj = JSON.parse(targetResponse.responseText);
console.log(obj.uploadImageId);
} else {
console.log("fail");
}
}
var buffer = str2ab_blobreader(file, function(buf) {
xhrNativeObject.send(buf);
});
quand j'utilise le code dans mon contrôleur Grails ça a bien fonctionné mais quand je l'utilise dans une ressource REST je reçois toujours: request n'est pas une MultipartHttpServletRequest ou SecurityContextHolderAwareRequestwrapper
la sortie log
request: com.sun.proxy.$Proxy58
l'envoi d'un blob de fichier à partir de JavaScript j'utilise XMLHttpRequest
qui contient le blob dans le corps et quelques paramètres de requête.
Comment faire fonctionner le téléchargement de fichiers JAX-RS? Comment puis-je recevoir des params de requête supplémentaires avec ma requête POST?
4 réponses
Côté Serveur, vous pouvez utiliser quelque chose comme ceci
@POST
@Path("/fileupload") //Your Path or URL to call this service
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(
@DefaultValue("true") @FormDataParam("enabled") boolean enabled,
@FormDataParam("file") InputStream uploadedInputStream,
@FormDataParam("file") FormDataContentDisposition fileDetail) {
//Your local disk path where you want to store the file
String uploadedFileLocation = "D://uploadedFiles/" + fileDetail.getFileName();
System.out.println(uploadedFileLocation);
// save it
File objFile=new File(uploadedFileLocation);
if(objFile.exists())
{
objFile.delete();
}
saveToFile(uploadedInputStream, uploadedFileLocation);
String output = "File uploaded via Jersey based RESTFul Webservice to: " + uploadedFileLocation;
return Response.status(200).entity(output).build();
}
private void saveToFile(InputStream uploadedInputStream,
String uploadedFileLocation) {
try {
OutputStream out = null;
int read = 0;
byte[] bytes = new byte[1024];
out = new FileOutputStream(new File(uploadedFileLocation));
while ((read = uploadedInputStream.read(bytes)) != -1) {
out.write(bytes, 0, read);
}
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
encore une fois cela peut être vérifié avec le code client en java avec
public class TryFile {
public static void main(String[] ar)
throws HttpException, IOException, URISyntaxException {
TryFile t = new TryFile();
t.method();
}
public void method() throws HttpException, IOException, URISyntaxException {
String url = "http://localhost:8080/...../fileupload"; //Your service URL
String fileName = ""; //file name to be uploaded
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(url);
FileBody fileContent = new FiSystem.out.println("hello");
StringBody comment = new StringBody("Filename: " + fileName);
MultipartEntity reqEntity = new MultipartEntity();
reqEntity.addPart("file", fileContent);
httppost.setEntity(reqEntity);
HttpResponse response = httpclient.execute(httppost);
HttpEntity resEntity = response.getEntity();
}
}
avec HTML, vous pouvez simplement vérifier avec ce code
<html>
<body>
<h1>Upload File with RESTFul WebService</h1>
<form action="<Your service URL (htp://localhost:8080/.../fileupload)" method="post" enctype="multipart/form-data">
<p>
Choose a file : <input type="file" name="file" />
</p>
<input type="submit" value="Upload" />
</form>
pour obtenir QueryParam, cochez @QueryParam ou pour l'en-tête param utilisez @HeaderParam
essayez ceci, j'espère que cela vous aidera toi avec ton problème.
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(@Multipart(value = "vendor") String vendor,
@Multipart(value = "uploadedFile") Attachment attr) {
alors que ce qui suit est le même pour Jersey (FormDataParam est une extension de Jersey):
@Consumes(MediaType.MULTIPART_FORM_DATA_TYPE)
public String postForm(
@DefaultValue("true") @FormDataParam("enabled") boolean enabled,
@FormDataParam("data") FileData bean,
@FormDataParam("file") InputStream file,
@FormDataParam("file") FormDataContentDisposition fileDisposition) {
(j'ai ignoré le @ Path, @POST et @Produces, et d'autres annotations non pertinentes.)
Ajouter enctype="multipart/form-data"
dans votre forme de code de submitter et @Consumes(MediaType.MULTIPART_FORM_DATA_TYPE)
sur votre méthode @POST pour que nous sachions que nous soumettons un fichier multipart et que l'api rest peut le consommer.
Votre méthode rest api pourrait ressembler à
@POST
@Path("/uploadfile")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response upload(
@FormDataParam("file") InputStream fileInputStream,
@FormDataParam("file") FormDataContentDisposition disposition) {
//...
}
ou
@POST
@Path("/uploadfile")
public void post(File file) {
Reader reader = new Reader(new FileInputStream(file));
// ...
}
cela créera un fichier temporaire sur le serveur. Il lit à partir du réseau et sauvegarde dans le fichier temporaire.
Au programme, sur la défensive, je vérifierais les métadonnées du type de contenu du fichier étant téléchargé.
Voici ce que nous avons fait pour télécharger le fichier (images, dans notre cas) :
côté Serveur
@POST
@RolesAllowed("USER")
@Path("/upload")
@Consumes("multipart/form-data")
public Response uploadFile(MultipartFormDataInput input) throws IOException
{
File local;
final String UPLOADED_FILE_PATH = filesRoot; // Check applicationContext-Server.properties file
//Get API input data
Map<String, List<InputPart>> uploadForm = input.getFormDataMap();
//The file name
String fileName;
String pathFileName = "";
//Get file data to save
List<InputPart> inputParts = uploadForm.get("attachment");
try
{
for (InputPart inputPart : inputParts)
{
//Use this header for extra processing if required
MultivaluedMap<String, String> header = inputPart.getHeaders();
fileName = getFileName(header);
String tmp = new SimpleDateFormat("yyyyMMddhhmmss").format(new Date());
pathFileName = "images/upload/" + tmp + '_' + fileName + ".png";
fileName = UPLOADED_FILE_PATH + pathFileName;
// convert the uploaded file to input stream
InputStream inputStream = inputPart.getBody(InputStream.class, null);
byte[] bytes = IOUtils.toByteArray(inputStream);
// constructs upload file path
writeFile(bytes, fileName);
// NOTE : The Target picture boundary is 800x600. Should be specified somewhere else ?
BufferedImage scaledP = getScaledPicture(fileName, 800, 600, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false);
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(scaledP, "png", os);
local = new File(fileName);
ImageIO.write(scaledP, "png", local);
}
}
catch (Exception e)
{
e.printStackTrace();
return Response.serverError().build();
}
return Response.status(201).entity(pathFileName).build();
}
côté client, nous utilisons AngularJS qui est codé par une autre équipe. Je ne vais pas être en mesure d'expliquer à ce sujet, mais voici le code :
$scope.setPicture = function (element)
{
var t = new Date();
console.log(t + ' - ' + t.getMilliseconds());
// Only process image files.
if (!element[0].type.match('image.*'))
{
console.log('File is not an image');
Error.current.element = $document[0].getElementById('comet-project-upload');
Error.current.message = 'Please select a picture.';
$scope.$apply();
}
else if (element[0].size > 10 * 1024 * 1024)
{
console.log('File is too big');
Error.current.element = $document[0].getElementById('comet-project-upload');
Error.current.message = 'File is too big. Please select another file.';
$scope.$apply();
}
else
{
self.animSpinner = true;
var fd = new FormData();
//Take the first file
fd.append('attachment', element[0]);
//Note : attachment is the compulsory name ?
Project.uploadImage(fd).then(
function (data)
{
self.animSpinner = false;
// self.$apply not needed because $digest already in progress
self.projectPicture = data;
},
function ()
{
self.animSpinner = false;
Error.current.element = $document[0].getElementById('comet-project-upload');
Error.current.message = 'Error with the server when uploading the image';
console.error('Picture Upload failed! ' + status + ' ' + headers + ' ' + config);
}
);
}
};
et la fonction uploadImage:
this.uploadImage = function (imageData)
{
var deferred = $q.defer();
$http.post('/comet/api/image/upload', imageData,
{
headers: { 'Content-Type': undefined, Authorization: User.hash },
//This method will allow us to change how the data is sent up to the server
// for which we'll need to encapsulate the model data in 'FormData'
transformRequest: angular.identity
//The cool part is the undefined content-type and the transformRequest: angular.identity
// that give at the $http the ability to choose the right "content-type" and manage
// the boundary needed when handling multipart data.
})
.success(function (data/*, status, headers, config*/)
{
deferred.resolve(data);
})
.error(function (data, status, headers, config)
{
console.error('Picture Upload failed! ' + status + ' ' + headers + ' ' + config);
deferred.reject();
});
return deferred.promise;
};
j'Espère qu'il vous aidera ...