web-dev-qa-db-ja.com

JAX-RSを使用したFileUpload

JavaScriptクライアントからJAX-RS Javaサーバーにファイルをアップロードしようとしています。

私は次のRESTサーバーでアップロード機能を使用します:

@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)
    }
}

クライアント側では、次のようにファイルを送信します。

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

Grailsコントローラーでコードを使用するとうまくいきましたが、RESTリソースで使用すると常に取得されます。リクエストはMultipartHttpServletRequestまたはSecurityContextHolderAwareRequestWrapperではありません

ログ出力は

request: com.Sun.proxy.$Proxy58

JavaScriptからファイルblobを送信するには、XMLHttpRequestを使用します。これには、本文にblobといくつかのクエリパラメーターが含まれています。

JAX-RSファイルのアップロードを機能させるにはどうすればよいですか?POST request?

22
confile

サーバー側では、このようなものを使用できます

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

}

繰り返しますが、これはJavaのクライアントコードで確認できます

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

HTMLを使用すると、このコードで簡単に確認できます

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

QueryParamを取得するには、@ QueryParamを確認するか、ヘッダーパラメーターに@HeaderParamを使用します

@ QueryParamの例

@ HeaderParamの例

これを試して、これがあなたの問題に役立つことを願っています。

39
ajitksharma

これを行うJax-RSの方法はありません。各サーバーには独自の拡張機能があり、すべてマルチパートフォーム送信を使用します。たとえば、CXFでは、次を使用すると、マルチパートフォームを介してアップロードできます。 (添付ファイルはCXF固有の拡張機能です)

@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(@Multipart(value = "vendor") String vendor,
        @Multipart(value = "uploadedFile") Attachment attr) {

一方、以下はJerseyでも同じです(FormDataParamは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) {

(@ Path、@ POST、@ Produces、およびその他の無関係なアノテーションは無視しました。)

12
Jeff Wang

フォーム送信コードに_enctype="multipart/form-data"_を追加し、@ POSTメソッドに@Consumes(MediaType.MULTIPART_FORM_DATA_TYPE)を追加して、マルチパートファイルを送信し、残りのAPIがそれを使用できるようにします。残りのapiメソッドは次のようになります

_@POST
@Path("/uploadfile")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response upload(
    @FormDataParam("file") InputStream fileInputStream, 
    @FormDataParam("file") FormDataContentDisposition disposition) {
        //...
}
_

または

_ @POST
 @Path("/uploadfile")
 public void post(File file) {
    Reader reader = new Reader(new FileInputStream(file));
    // ... 
 }
_

これにより、サーバー上に一時ファイルが作成されます。ネットワークから読み取り、一時ファイルに保存します。

防御的にプログラムするには、アップロードするファイルのコンテンツタイプメタデータを確認します

8
Atul

ファイルをアップロードするために私たちがしたことは次のとおりです(この例では画像)。
サーバー側

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

}

クライアント側では、別のチームによってコーディングされたAngularJSを使用します。私はそれについて説明することはできませんが、ここにコードがあります:

    $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);
      }
    );
  }
};

そして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;
};

それがあなたを助けることを願っています...

5
Snorky35