Apache Jersey Client Multipart Upload Mp4 as Octlet-stream?

Every RESTful API grows to the point information technology needs to support file uploads. Supporting file uploads with Jersey is rather simple and in this mail I will show you how to practise information technology. All my posts come with a GitHub project, and this mail service is no exception.

The GitHub repository for this post tin can be found at:

  • https://github.com/bytefish/JerseyFileUploadExample

Dependencies

Yous need the bailiwick of jersey-media-multipart package to support HTTP multipart requests with Jersey. I am using Jump Boot for the example, so you volition also demand the spring-boot-starter-jersey depdendency.

In the dependencies element of the POM file we add together both dependencies:

                                      <dependency>              <groupId>org.springframework.boot</groupId>              <artifactId>bound-kicking-starter-jersey</artifactId>              </dependency>              <dependency>              <groupId>org.glassfish.jersey.media</groupId>              <artifactId>bailiwick of jersey-media-multipart</artifactId>              <version>ii.25</version>              </dependency>                      

Project Construction

Information technology'southward useful to take a expect at the project construction offset:

Project Overview

The purpose of the various classes:

  • exceptions
    • FileUploadException
      • An exception, that volition be thrown if a File Upload fails due to an fault.
  • handler
    • IFileUploadHandler
      • This interface needs to be implemented to handle incoming file upload requests.
    • LocalStorageHandler
      • A default implementation of the IFileUploadHandler, which writes a file to the local filesystem.
  • model
    • errors
      • ServiceError
        • An error containing an error code and error reason for failed uploads.
      • HttpServiceError
        • Wraps a ServiceError and adds a HTTP Condition Code, which will be sent dorsum to the client.
    • files
      • HttpFile
        • Abstracts the incoming HTTP multipart file data into something more than usable for business logic.
    • asking
      • FileUploadRequest
        • Abstracts the incoming HTTP multipart class information and builds a Request with Metadata and a HttpFile.
    • response
      • FileUploadResponse
        • The response information, that will be sent to the client.
  • provider
    • IRootPathProvider
      • This interface needs to be implemented for defining the Servers root path.
    • RootPathProvider
      • The default implementation of the IRootPathProvider.
  • web
    • configuration
      • JerseyConfiguration
        • The Bailiwick of jersey configuration setting up the Jersey server.
    • exceptions
      • FileUploadExceptionMapper
        • Handles Exceptions on the highest level and turns them into a useful representation.
    • resource
      • FileUploadResource
        • Finally the resource implementing the File Upload API.
  • SampleJerseyApplication
    • Configures and starts the Server.

Domain Model

Errors

In nearly of my posts I stress how of import errors are. It's because you need to provide a proper approach to errors as early equally possible in your projects. It should be possible for a client to make sense of errors, so you don't have to investigate log files for every problem.

And more importantly, you lot don't want to leak Exception details to the client. Your Stacktrace may contain sensitive information and may reveal details virtually your architecture, that should be hidden. And I approximate you lot don't desire your exception letters to exist sent into the wild.

ServiceError

An error sent to the client should incorporate a code and an error message. Providing a specific fault code makes it possible for a client to automatically evaluate the error response and have the right actions, such as notifying users to provide required data.

                                      // Copyright (c) Philipp Wagner. All rights reserved.              // Licensed under the MIT license. Come across LICENSE file in the project root for full license information.              bundle              de.bytefish.fileuploads.model.errors              ;              import              com.fasterxml.jackson.annotation.JsonProperty              ;              public              class              ServiceError              {              private              final              String              code              ;              individual              concluding              String              message              ;              public              ServiceError              (              String              code              ,              String              message              )              {              this              .              lawmaking              =              lawmaking              ;              this              .              message              =              bulletin              ;              }              @JsonProperty              (              "code"              )              public              Cord              getCode              ()              {              return              code              ;              }              @JsonProperty              (              "message"              )              public              String              getMessage              ()              {              return              bulletin              ;              }              }                      

HttpServiceError

The API volition exist a RESTful API, which defined HTTP Status Codes to indicate failure and success. There may be different types of errors, like a bad request (HTTP Status 400), invalid authentication credentials (HTTP Condition 401). It should also exist possible to send an Internal Server Error (HTTP Status 500) to the client, which says the error is on the Server side and cannot be fixed by the client.

                                      // Copyright (c) Philipp Wagner. All rights reserved.              // Licensed under the MIT license. Encounter LICENSE file in the project root for total license information.              package              de.bytefish.fileuploads.model.errors              ;              public              class              HttpServiceError              {              private              final              int              httpStatusCode              ;              private              concluding              ServiceError              serviceError              ;              public              HttpServiceError              (              int              httpStatusCode              ,              ServiceError              serviceError              )              {              this              .              httpStatusCode              =              httpStatusCode              ;              this              .              serviceError              =              serviceError              ;              }              public              int              getHttpStatusCode              ()              {              return              httpStatusCode              ;              }              public              ServiceError              getServiceError              ()              {              return              serviceError              ;              }              }                      

FileUploadException

If a file can't be uploaded due to missing or invalid data, and so the control menstruum will be exited by throwing an awarding. The FileUploadException gets a ServiceError passed into and wraps information technology in a HttpServiceError. If someone in the application thinks it's appropriate to handle the exception, then the method getHttpServiceError() returns the error reason.

                                      // Copyright (c) Philipp Wagner. All rights reserved.              // Licensed under the MIT license. Run into LICENSE file in the projection root for full license information.              package              de.bytefish.fileuploads.exceptions              ;              import              de.bytefish.fileuploads.model.errors.HttpServiceError              ;              import              de.bytefish.fileuploads.model.errors.ServiceError              ;              public              class              FileUploadException              extends              RuntimeException              {              private              final              HttpServiceError              httpServiceError              ;              public              FileUploadException              (              ServiceError              serviceError              )              {              this              .              httpServiceError              =              createServiceError              (              serviceError              );              }              public              FileUploadException              (              ServiceError              serviceError              ,              Cord              message              )              {              super              (              message              );              this              .              httpServiceError              =              createServiceError              (              serviceError              );              }              public              FileUploadException              (              ServiceError              serviceError              ,              String              message              ,              Throwable              cause              )              {              super              (              bulletin              ,              crusade              );              this              .              httpServiceError              =              createServiceError              (              serviceError              );              }              public              HttpServiceError              getHttpServiceError              ()              {              return              httpServiceError              ;              }              private              static              HttpServiceError              createServiceError              (              ServiceError              serviceError              )              {              return              new              HttpServiceError              (              400              ,              serviceError              );              }              }                      

FileUploadExceptionMapper

The best place to handle such an exception is in the web layer. Jersey provides a so called ExceptionMapper, which makes it possible to handle specific exceptions. In the instance the FileUploadException is handled and a response is generated, with the HTTP Status code and ServiceError extracted from the exception.

                                      bundle              de.bytefish.fileuploads.web.exceptions              ;              import              de.bytefish.fileuploads.exceptions.FileUploadException              ;              import              de.bytefish.fileuploads.model.errors.HttpServiceError              ;              import              org.slf4j.Logger              ;              import              org.slf4j.LoggerFactory              ;              import              javax.ws.rs.core.Response              ;              public              grade              FileUploadExceptionMapper              implements              javax              .              ws              .              rs              .              ext              .              ExceptionMapper              <              FileUploadException              >              {              private              static              final              Logger              logger              =              LoggerFactory              .              getLogger              (              FileUploadExceptionMapper              .              grade              );              @Override              public              Response              toResponse              (              FileUploadException              fileUploadException              )              {              if              (              logger              .              isErrorEnabled              ())              {              logger              .              mistake              (              "An error occured"              ,              fileUploadException              );              }              HttpServiceError              httpServiceError              =              fileUploadException              .              getHttpServiceError              ();              return              Response              .              status              (              httpServiceError              .              getHttpStatusCode              ())              .              entity              (              httpServiceError              .              getServiceError              ())              .              build              ();              }              }                      

Abstracting the Multipart File Data

HttpFile

Even minor applications tin can grow into larger systems. So y'all want to decouple your system as early as possible in a project. 1 of these abstractions should be turning the incoming HTTP multipart request into something more useful. Considering you really don't want to fiddle around with a HttpServletRequest deep down in your business logic; nor should whatever other developer in your team do so.

                                      // Copyright (c) Philipp Wagner. All rights reserved.              // Licensed under the MIT license. See LICENSE file in the projection root for full license information.              bundle              de.bytefish.fileuploads.model.files              ;              import              java.io.InputStream              ;              import              java.util.Map              ;              public              grade              HttpFile              {              private              final              String              proper noun              ;              private              final              String              submittedFileName              ;              private              final              long              size              ;              private              final              Map              <              String              ,              String              >              parameters              ;              private              final              InputStream              stream              ;              public              HttpFile              (              String              name              ,              Cord              submittedFileName              ,              long              size              ,              Map              <              Cord              ,              String              >              parameters              ,              InputStream              stream              )              {              this              .              proper name              =              name              ;              this              .              submittedFileName              =              submittedFileName              ;              this              .              size              =              size              ;              this              .              parameters              =              parameters              ;              this              .              stream              =              stream              ;              }              public              String              getName              ()              {              return              name              ;              }              public              String              getSubmittedFileName              ()              {              render              submittedFileName              ;              }              public              long              getSize              ()              {              return              size              ;              }              public              Map              <              String              ,              String              >              getParameters              ()              {              return              parameters              ;              }              public              InputStream              getStream              ()              {              render              stream              ;              }              }                      

FileUploadRequest

Every file upload may contain some metadata similar a title or the description. This metadata shouldn't pollute the HttpFile, and so nosotros wrap the HttpFile and add together the Metadata in a FileUploadRequest.

                                      // Copyright (c) Philipp Wagner. All rights reserved.              // Licensed nether the MIT license. Encounter LICENSE file in the projection root for full license information.              package              de.bytefish.fileuploads.model.request              ;              import              de.bytefish.fileuploads.model.files.HttpFile              ;              public              class              FileUploadRequest              {              private              last              Cord              title              ;              individual              final              String              description              ;              individual              final              HttpFile              httpFile              ;              public              FileUploadRequest              (              Cord              title              ,              String              description              ,              HttpFile              httpFile              )              {              this              .              title              =              championship              ;              this              .              clarification              =              description              ;              this              .              httpFile              =              httpFile              ;              }              public              String              getTitle              ()              {              return              title              ;              }              public              String              getDescription              ()              {              return              clarification              ;              }              public              HttpFile              getHttpFile              ()              {              return              httpFile              ;              }              }                      

FileUploadResponse

If you are storing a file on the server, so chances are good there are indistinguishable file names when using the original file name. These files shouldn't exist overriden, so one fashion is to assign a unique identifier to the file, and laissez passer it to the client. The client itself then knows, which file he has to request.

                                      // Copyright (c) Philipp Wagner. All rights reserved.              // Licensed nether the MIT license. See LICENSE file in the project root for full license information.              package              de.bytefish.fileuploads.model.response              ;              import              com.fasterxml.jackson.annotation.JsonProperty              ;              public              class              FileUploadResponse              {              private              final              Cord              identifier              ;              public              FileUploadResponse              (              String              identifier              )              {              this              .              identifier              =              identifier              ;              }              @JsonProperty              (              "identifier"              )              public              String              getIdentifier              ()              {              return              identifier              ;              }              }                      

Treatment the incoming data

Determining the Root Path of the Server

The user should be able to decide, where to write files and this is where the IRootPathProvider is necessary. An IRootPathProvider is a simple interface, that but returns the root path to write the files to, if the implementation is using a local filesystem.

                                      // Copyright (c) Philipp Wagner. All rights reserved.              // Licensed under the MIT license. Encounter LICENSE file in the project root for total license information.              package              de.bytefish.fileuploads.provider              ;              public              interface              IRootPathProvider              {              Cord              getRootPath              ();              }                      

The default implementation RootPathProvider requires the root path to be configured explicitly, when bootstrapping the server.

                                      // Copyright (c) Philipp Wagner. All rights reserved.              // Licensed under the MIT license. See LICENSE file in the project root for total license information.              package              de.bytefish.fileuploads.provider              ;              public              class              RootPathProvider              implements              IRootPathProvider              {              private              concluding              String              path              ;              public              RootPathProvider              (              String              path              )              {              this              .              path              =              path              ;              }              @Override              public              String              getRootPath              ()              {              return              path              ;              }              }                      

FileUploadHandler

With a proper model in place, the abstraction for handling a FileUploadRequest becomes elementary:

                                      // Copyright (c) Philipp Wagner. All rights reserved.              // Licensed under the MIT license. Come across LICENSE file in the projection root for full license data.              package              de.bytefish.fileuploads.handler              ;              import              de.bytefish.fileuploads.model.request.FileUploadRequest              ;              import              de.bytefish.fileuploads.model.response.FileUploadResponse              ;              public              interface              IFileUploadHandler              {              FileUploadResponse              handle              (              FileUploadRequest              request              );              }                      

The default implementation writes to the local filesystem, that's why I called it a LocalStorageFileUploadHandler. You can run across, that the handler gets an IRootPathProvider injected, so you can hands configure the root path from the outside.

The LocalStorageFileUploadHandler kickoff evaluates, if a file is bachelor in the request. If some assertions don't agree, the control flow volition be stopped and a FileUploadException is thrown, with a ServiceError included.

                                      // Copyright (c) Philipp Wagner. All rights reserved.              // Licensed under the MIT license. Meet LICENSE file in the project root for full license data.              package              de.bytefish.fileuploads.handler              ;              import              de.bytefish.fileuploads.exceptions.FileUploadException              ;              import              de.bytefish.fileuploads.model.files.HttpFile              ;              import              de.bytefish.fileuploads.model.errors.ServiceError              ;              import              de.bytefish.fileuploads.model.request.FileUploadRequest              ;              import              de.bytefish.fileuploads.model.response.FileUploadResponse              ;              import              de.bytefish.fileuploads.provider.IRootPathProvider              ;              import              org.springframework.beans.manufactory.annotation.Autowired              ;              import              org.springframework.stereotype.Component              ;              import              java.io.InputStream              ;              import              coffee.nio.file.Files              ;              import              coffee.nio.file.Paths              ;              import              coffee.util.UUID              ;              @Component              public              course              LocalStorageFileUploadHandler              implements              IFileUploadHandler              {              private              final              IRootPathProvider              rootPathProvider              ;              @Autowired              public              LocalStorageFileUploadHandler              (              IRootPathProvider              rootPathProvider              )              {              this              .              rootPathProvider              =              rootPathProvider              ;              }              @Override              public              FileUploadResponse              handle              (              FileUploadRequest              request              )              {              // Early exit, if in that location is no Asking:              if              (              request              ==              zilch              )              {              throw              new              FileUploadException              (              new              ServiceError              (              "missingFile"              ,              "Missing File data"              ),              String              .              format              (              "Missing Parameter: request"              ));              }              // Become the HttpFile:              HttpFile              httpFile              =              asking              .              getHttpFile              ();              // Early exit, if the Request has no data assigned:              if              (              httpFile              ==              nix              )              {              throw              new              FileUploadException              (              new              ServiceError              (              "missingFile"              ,              "Missing File data"              ),              String              .              format              (              "Missing Parameter: request.httpFile"              ));              }              // We don't override existing files, create a new UUID File name:              String              targetFileName              =              UUID              .              randomUUID              ().              toString              ();              // Write information technology to Disk:              internalWriteFile              (              httpFile              .              getStream              (),              targetFileName              );              return              new              FileUploadResponse              (              targetFileName              );              }              private              void              internalWriteFile              (              InputStream              stream              ,              String              fileName              )              {              attempt              {              Files              .              copy              (              stream              ,              Paths              .              get              (              rootPathProvider              .              getRootPath              (),              fileName              ));              }              catch              (              Exception              e              )              {              throw              new              FileUploadException              (              new              ServiceError              (              "storingFileError"              ,              "Error writing file"              ),              String              .              format              (              "Writing File '%s' failed"              ,              fileName              ),              eastward              );              }              }              }                      

The Web Layer

Resource

The FileUploadResource at present connects all the parts. It defines an endpoint, that consumes multipart form data and produces a JSON Response. The fileUpload(...) method uses the @FormDataParam annotation from the jersey-media-multipart bundle to evaluate the incoming HTTP multipart class data request.

From the incoming request we first build the HttpFile, the FileUploadRequest and then pass it into the configured IFileUploadHandler. If the file was written successfully, then the FileUploadResponse with a unique file identifier is returned to the client.

                                      // Copyright (c) Philipp Wagner. All rights reserved.              // Licensed under the MIT license. Come across LICENSE file in the project root for full license data.              package              de.bytefish.fileuploads.spider web.resource              ;              import              de.bytefish.fileuploads.handler.IFileUploadHandler              ;              import              de.bytefish.fileuploads.model.files.HttpFile              ;              import              de.bytefish.fileuploads.model.request.FileUploadRequest              ;              import              de.bytefish.fileuploads.model.response.FileUploadResponse              ;              import              org.glassfish.jersey.media.multipart.FormDataContentDisposition              ;              import              org.glassfish.bailiwick of jersey.media.multipart.FormDataParam              ;              import              org.springframework.beans.factory.notation.Autowired              ;              import              org.springframework.stereotype.Component              ;              import              javax.ws.rs.Consumes              ;              import              javax.ws.rs.POST              ;              import              javax.ws.rs.Path              ;              import              javax.ws.rs.Produces              ;              import              javax.ws.rs.core.MediaType              ;              import              javax.ws.rs.core.Response              ;              import              coffee.io.InputStream              ;              @Component              @Path              (              "/files"              )              public              form              FileUploadResource              {              private              concluding              IFileUploadHandler              fileUploadHandler              ;              @Autowired              public              FileUploadResource              (              IFileUploadHandler              fileUploadHandler              )              {              this              .              fileUploadHandler              =              fileUploadHandler              ;              }              @Post              @Path              (              "/upload"              )              @Consumes              (              MediaType              .              MULTIPART_FORM_DATA              )              @Produces              (              MediaType              .              APPLICATION_JSON              )              public              Response              fileUpload              (              @FormDataParam              (              "title"              )              Cord              championship              ,              @FormDataParam              (              "description"              )              String              description              ,              @FormDataParam              (              "file"              )              InputStream              stream              ,              @FormDataParam              (              "file"              )              FormDataContentDisposition              fileDetail              )              {              // Create the HttpFile:              HttpFile              httpFile              =              new              HttpFile              (              fileDetail              .              getName              (),              fileDetail              .              getFileName              (),              fileDetail              .              getSize              (),              fileDetail              .              getParameters              (),              stream              );              // Create the FileUploadRequest:              FileUploadRequest              fileUploadRequest              =              new              FileUploadRequest              (              title              ,              description              ,              httpFile              );              // Handle the File Upload:              FileUploadResponse              result              =              fileUploadHandler              .              handle              (              fileUploadRequest              );              return              Response              .              status              (              200              )              .              entity              (              result              )              .              build              ();              }              }                      

Configuration

Nigh done! In the JerseyConfig nosotros demand to enable the MultiPartFeature, annals the FileUploadResource and register the FileUploadExceptionMapper.

                                      package              de.bytefish.fileuploads.web.configuration              ;              import              de.bytefish.fileuploads.spider web.exceptions.FileUploadExceptionMapper              ;              import              de.bytefish.fileuploads.web.resource.FileUploadResource              ;              import              org.glassfish.jersey.media.multipart.MultiPartFeature              ;              import              org.glassfish.jersey.server.ResourceConfig              ;              import              org.springframework.beans.manufacturing plant.annotation.Autowired              ;              import              org.springframework.stereotype.Component              ;              @Component              public              class              JerseyConfig              extends              ResourceConfig              {              public              JerseyConfig              ()              {              // Annals the Resource:              register              (              FileUploadResource              .              form              );              // Annals the Characteristic for Multipart Uploads (File Upload):              register              (              MultiPartFeature              .              grade              );              // Register Exception Mappers for returning API Errors:              register              (              FileUploadExceptionMapper              .              class              );              // Uncomment to disable WADL Generation:              //property("jersey.config.server.wadl.disableWadl", true);              // Uncomment to add Asking Tracing:              //property("jersey.config.server.tracing.blazon", "ALL");              //property("bailiwick of jersey.config.server.tracing.threshold", "TRACE");              }              }                      

Bound Boot Awarding

Finally the SpringBootApplication tin exist defined, so the Spring container is configured and the Webserver is booted. And then much dependencies, but this looks simple correct? It's considering the example only has one implementation for nearly of the dependencies, then Jump automatically resolves them.

The just dependency, that needs to be declared explicitly is the IRootPathProvider. I have decided to write to a binder named out in the Webserver directory. You can adjust the path to your needs.

                                      // Copyright (c) Philipp Wagner. All rights reserved.              // Licensed under the MIT license. Come across LICENSE file in the project root for full license data.              package              de.bytefish.fileuploads              ;              import              de.bytefish.fileuploads.provider.IRootPathProvider              ;              import              de.bytefish.fileuploads.provider.RootPathProvider              ;              import              org.springframework.boot.autoconfigure.SpringBootApplication              ;              import              org.springframework.boot.builder.SpringApplicationBuilder              ;              import              org.springframework.boot.web.support.SpringBootServletInitializer              ;              import              org.springframework.context.notation.Edible bean              ;              @SpringBootApplication              public              form              SampleJerseyApplication              extends              SpringBootServletInitializer              {              public              static              void              master              (              String              []              args              )              {              new              SampleJerseyApplication              ()              .              configure              (              new              SpringApplicationBuilder              (              SampleJerseyApplication              .              class              ))              .              run              (              args              );              }              @Edible bean              IRootPathProvider              rootPathProvider              ()              {              return              new              RootPathProvider              (              "./out"              );              }              }                      

Example

To upload a file y'all can use curl. In the example I am uploading a file myfile.txt to the Webserver:

                                      curl              --              verbose              --              form              championship              =              "File Championship"              --              grade              description              =              "File Description"              --              grade              file              =              @"myfile.txt"              http              :              //localhost:8080/files/upload                      

costellocionfibed.blogspot.com

Source: https://www.bytefish.de/blog/file_upload_api_jersey.html

0 Response to "Apache Jersey Client Multipart Upload Mp4 as Octlet-stream?"

Postar um comentário

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel