【问题标题】:How to find HTTP Media Type (MIME type) from response?如何从响应中查找 HTTP 媒体类型(MIME 类型)?
【发布时间】:2012-01-31 10:26:34
【问题描述】:

在使用 Apache HTTP Client v4 发出 GET 请求时,如何获取响应媒体类型(正式为 MIME 类型)?

使用 Apache HTTP Client v3,通过以下方式获得 MIME 类型:

 String mimeType = response.getMimeType();

如何使用 Apache HTTP Client v4 获取媒体类型?

【问题讨论】:

    标签: java httpclient apache-httpcomponents


    【解决方案1】:

    要从响应中获取内容类型,您可以使用 ContentType 类。

    HttpEntity entity = response.getEntity();
    ContentType contentType;
    if (entity != null) 
        contentType = ContentType.get(entity);
    

    使用这个类你可以很容易地提取 mime 类型:

    String mimeType = contentType.getMimeType();
    

    或字符集:

    Charset charset = contentType.getCharset();
    

    【讨论】:

    • 对于 Android 开发者:ContentType 类在 Apache HTTP 库的 Android 端口中不可用
    • 对于Android,我想可以使用相关的解析器类developer.android.com/reference/org/apache/http/message/…
    • 我不明白为什么response.getEntity().getContentType() 不返回ContentType 实例。
    【解决方案2】:

    “Content-type”HTTP 标头应该为您提供 mime 类型信息:

    Header contentType = response.getFirstHeader("Content-Type");
    

    或作为

    Header contentType = response.getEntity().getContentType();
    

    然后您可以提取 mime 类型本身,因为内容类型也可能包含编码。

    String mimeType = contentType.getValue().split(";")[0].trim();
    

    当然,在获取标头的值之前不要忘记检查空值(以防服务器未发送内容类型标头)。

    【讨论】:

      【解决方案3】:

      请注意,Apache's ContentType 将 SVG 视为 application/svg+xml,而不是 IANA-defined image/svg+xml,这似乎是一个错误的分类。

      虽然这个答案没有直接回答问题,但它通过使用 Java 的 HTTP 客户端提供了使用 Apache 的 HTTP 客户端的替代方法。另外,示例代码:

      • 发出HEAD 请求,而不是GET 请求,这是一种更轻量级的操作;
      • 对 HTTP 请求引入了 5 秒的超时,根据您的情况,可能需要更长的时间;
      • 尝试通过将内容类型解析为MediaType枚举而不是字符串来为媒体类型添加强类型;和
      • 避免使用正则表达式解析简单字符串的开销;和
      • 定义了更多的媒体类型,尤其是图像,这些类型不是 Apache 的 ContentType 定义的。

      事不宜迟,这里有一些 Java 源文件可能会有所帮助。

      媒体类型

      基本枚举对 IANA 媒体类型进行编码。如果您添加更多官方编码,请更新此答案,以便所有人受益。请注意,R Markdown、R XML 和 YAML 没有正式定义,因此您可能希望删除它们。

      import static org.apache.commons.io.FilenameUtils.getExtension;
      
      public enum MediaType {
        APP_JAVA_OBJECT(
          APPLICATION, "x-java-serialized-object"
        ),
      
        FONT_OTF( "otf" ),
        FONT_TTF( "ttf" ),
      
        IMAGE_APNG( "apng" ),
        IMAGE_ACES( "aces" ),
        IMAGE_AVCI( "avci" ),
        IMAGE_AVCS( "avcs" ),
        IMAGE_BMP( "bmp" ),
        IMAGE_CGM( "cgm" ),
        IMAGE_DICOM_RLE( "dicom_rle" ),
        IMAGE_EMF( "emf" ),
        IMAGE_EXAMPLE( "example" ),
        IMAGE_FITS( "fits" ),
        IMAGE_G3FAX( "g3fax" ),
        IMAGE_GIF( "gif" ),
        IMAGE_HEIC( "heic" ),
        IMAGE_HEIF( "heif" ),
        IMAGE_HEJ2K( "hej2k" ),
        IMAGE_HSJ2( "hsj2" ),
        IMAGE_X_ICON( "x-icon" ),
        IMAGE_JLS( "jls" ),
        IMAGE_JP2( "jp2" ),
        IMAGE_JPEG( "jpeg" ),
        IMAGE_JPH( "jph" ),
        IMAGE_JPHC( "jphc" ),
        IMAGE_JPM( "jpm" ),
        IMAGE_JPX( "jpx" ),
        IMAGE_JXR( "jxr" ),
        IMAGE_JXRA( "jxrA" ),
        IMAGE_JXRS( "jxrS" ),
        IMAGE_JXS( "jxs" ),
        IMAGE_JXSC( "jxsc" ),
        IMAGE_JXSI( "jxsi" ),
        IMAGE_JXSS( "jxss" ),
        IMAGE_KTX( "ktx" ),
        IMAGE_KTX2( "ktx2" ),
        IMAGE_NAPLPS( "naplps" ),
        IMAGE_PNG( "png" ),
        IMAGE_SVG_XML( "svg+xml" ),
        IMAGE_T38( "t38" ),
        IMAGE_TIFF( "tiff" ),
        IMAGE_WEBP( "webp" ),
        IMAGE_WMF( "wmf" ),
      
        TEXT_HTML( TEXT, "html" ),
        TEXT_MARKDOWN( TEXT, "markdown" ),
        TEXT_PLAIN( TEXT, "plain" ),
        TEXT_R_MARKDOWN( TEXT, "R+markdown" ),
        TEXT_R_XML( TEXT, "R+xml" ),
        TEXT_YAML( TEXT, "yaml" ),
      
        UNDEFINED( TypeName.UNDEFINED, "undefined" );
      
        /**
         * The IANA-defined types.
         */
        public enum TypeName {
          APPLICATION,
          IMAGE,
          TEXT,
          UNDEFINED
        }
      
        /**
         * The fully qualified IANA-defined media type.
         */
        private final String mMediaType;
      
        /**
         * The IANA-defined type name.
         */
        private final TypeName mTypeName;
      
        /**
         * The IANA-defined subtype name.
         */
        private final String mSubtype;
      
        /**
         * Constructs an instance using the default type name of "image".
         *
         * @param subtype The image subtype name.
         */
        MediaType( final String subtype ) {
          this( IMAGE, subtype );
        }
      
        /**
         * Constructs an instance using an IANA-defined type and subtype pair.
         *
         * @param typeName The media type's type name.
         * @param subtype  The media type's subtype name.
         */
        MediaType( final TypeName typeName, final String subtype ) {
          mTypeName = typeName;
          mSubtype = subtype;
          mMediaType = typeName.toString().toLowerCase() + '/' + subtype;
        }
      
        /**
         * Returns the {@link MediaType} associated with the given file.
         *
         * @param file Has a file name that may contain an extension associated with
         *             a known {@link MediaType}.
         * @return {@link MediaType#UNDEFINED} if the extension has not been
         * assigned, otherwise the {@link MediaType} associated with this
         * {@link File}'s file name extension.
         */
        public static MediaType valueFrom( final File file ) {
          return valueFrom( file.getName() );
        }
      
        /**
         * Returns the {@link MediaType} associated with the given file name.
         *
         * @param filename The file name that may contain an extension associated
         *                 with a known {@link MediaType}.
         * @return {@link MediaType#UNDEFINED} if the extension has not been
         * assigned, otherwise the {@link MediaType} associated with this
         * URL's file name extension.
         */
        public static MediaType valueFrom( final String filename ) {
          return getMediaType( getExtension( filename ) );
        }
      
        /**
         * Returns the {@link MediaType} for the given type and subtype names.
         *
         * @param type    The IANA-defined type name.
         * @param subtype The IANA-defined subtype name.
         * @return {@link MediaType#UNDEFINED} if there is no {@link MediaType} that
         * matches the given type and subtype names.
         */
        public static MediaType valueFrom(
          final String type, final String subtype ) {
          for( final var mediaType : MediaType.values() ) {
            if( mediaType.equals( type, subtype ) ) {
              return mediaType;
            }
          }
      
          return UNDEFINED;
        }
      
        /**
         * Answers whether the given type and subtype names equal this enumerated
         * value. This performs a case-insensitive comparison.
         *
         * @param type    The type name to compare against this {@link MediaType}.
         * @param subtype The subtype name to compare against this {@link MediaType}.
         * @return {@code true} when the type and subtype name match.
         */
        public boolean equals( final String type, final String subtype ) {
          return mTypeName.name().equalsIgnoreCase( type ) &&
            mSubtype.equalsIgnoreCase( subtype );
        }
      
        /**
         * Answers whether the given {@link TypeName} matches this type name.
         *
         * @param typeName The {@link TypeName} to compare against the internal value.
         * @return {@code true} if the given value is the same IANA-defined type name.
         */
        public boolean isType( final TypeName typeName ) {
          return mTypeName == typeName;
        }
      
        /**
         * Returns the IANA-defined type and sub-type.
         *
         * @return The unique media type identifier.
         */
        public String toString() {
          return mMediaType;
        }
      
        /**
         * Used by {@link MediaTypeExtensions} to initialize associations where the
         * subtype name and the file name extension have a 1:1 mapping.
         *
         * @return The IANA subtype value.
         */
        String getSubtype() {
          return mSubtype;
        }
      }
      

      媒体类型扩展

      不同的文件扩展名映射到不同的媒体类型。扩展名到MediaType 的映射并不一定意味着内容 匹配预期的媒体类型。应用程序必须小心读取文件头以确定实际的媒体类型。

      enum MediaTypeExtensions {
        MEDIA_FONT_OTF( FONT_OTF ),
        MEDIA_FONT_TTF( FONT_TTF ),
      
        MEDIA_IMAGE_APNG( IMAGE_APNG ),
        MEDIA_IMAGE_BMP( IMAGE_BMP ),
        MEDIA_IMAGE_GIF( IMAGE_GIF ),
        MEDIA_IMAGE_ICO( IMAGE_X_ICON, of( "ico", "cur" ) ),
        MEDIA_IMAGE_JPEG( IMAGE_JPEG, of( "jpg", "jpeg", "jfif", "pjpeg", "pjp" ) ),
        MEDIA_IMAGE_PNG( IMAGE_PNG ),
        MEDIA_IMAGE_SVG( IMAGE_SVG_XML, of( "svg" ) ),
        MEDIA_IMAGE_TIFF( IMAGE_TIFF, of( "tif", "tiff" ) ),
        MEDIA_IMAGE_WEBP( IMAGE_WEBP ),
      
        MEDIA_TEXT_MARKDOWN( TEXT_MARKDOWN, of(
          "md", "markdown", "mdown", "mdtxt", "mdtext", "mdwn", "mkd", "mkdown",
          "mkdn" ) ),
        MEDIA_TEXT_PLAIN( TEXT_PLAIN, of( "asc", "ascii", "txt", "text", "utxt" ) ),
        MEDIA_TEXT_R_MARKDOWN( TEXT_R_MARKDOWN, of( "Rmd" ) ),
        MEDIA_TEXT_R_XML( TEXT_R_XML, of( "Rxml" ) ),
        MEDIA_TEXT_YAML( TEXT_YAML, of( "yaml", "yml" ) );
      
        private final MediaType mMediaType;
        private final Set<String> mExtensions;
      
        MediaTypeExtensions( final MediaType mediaType ) {
          this( mediaType, of( mediaType.getSubtype() ) );
        }
      
        MediaTypeExtensions(
          final MediaType mediaType, final Set<String> extensions ) {
          assert mediaType != null;
          assert extensions != null;
          assert !extensions.isEmpty();
      
          mMediaType = mediaType;
          mExtensions = extensions;
        }
      
        static MediaType getMediaType( final String extension ) {
          final var sanitized = sanitize( extension );
      
          for( final var mediaType : MediaTypeExtensions.values() ) {
            if( mediaType.isType( sanitized ) ) {
              return mediaType.getMediaType();
            }
          }
      
          return UNDEFINED;
        }
      
        private boolean isType( final String sanitized ) {
          for( final var extension : mExtensions ) {
            if( extension.equalsIgnoreCase( sanitized ) ) {
              return true;
            }
          }
      
          return false;
        }
      
        private static String sanitize( final String extension ) {
          return extension == null ? "" : extension.toLowerCase();
        }
      
        private MediaType getMediaType() {
          return mMediaType;
        }
      }
      

      HttpMediaType

      最后,我们可以编写一个小型解析器,将内容类型标头转换为MediaType 值。请注意,HttpClient API 本身对标头名称执行区分大小写的比较,因此我们不能使用像 firstValueallValues 这样的方法,因为我们不知道服务器会返回“Content-Type”还是“内容类型”。严格来说,这似乎是一个错误,因为RFC-2616 声明邮件头不区分大小写。

      public class HttpMediaType {
      
        private final static HttpClient HTTP_CLIENT = HttpClient
          .newBuilder()
          .connectTimeout( ofSeconds( 5 ) )
          .followRedirects( NORMAL )
          .build();
      
        /**
         * Performs an HTTP HEAD request to determine the media type based on the
         * Content-Type header returned from the server.
         *
         * @param uri Determine the media type for this resource.
         * @return The data type for the resource or {@link MediaType#UNDEFINED} if
         * unmapped.
         * @throws MalformedURLException The {@link URI} could not be converted to
         *                               a {@link URL}.
         */
        public static MediaType valueFrom( final URI uri )
          throws MalformedURLException {
          final var mediaType = new MediaType[]{UNDEFINED};
      
          try {
            final var request = HttpRequest
              .newBuilder( uri )
              .method( "HEAD", noBody() )
              .build();
            final var response = HTTP_CLIENT.send( request, discarding() );
            final var headers = response.headers();
            final var map = headers.map();
      
            map.forEach( ( key, values ) -> {
              if( "Content-Type".equalsIgnoreCase( key ) ) {
                var header = values.get( 0 );
                // Trim off the character encoding.
                var i = header.indexOf( ';' );
                header = header.substring( 0, i == -1 ? header.length() : i );
      
                // Split the type and subtype.
                i = header.indexOf( '/' );
                i = i == -1 ? header.length() : i;
                final var type = header.substring( 0, i );
                final var subtype = header.substring( i + 1 );
      
                mediaType[ 0 ] = MediaType.valueFrom( type, subtype );
              }
            } );
          } catch( final Exception ex ) {
            // TODO: Inform the user?
          }
      
          return mediaType[ 0 ];
        }
      }
      

      【讨论】:

        猜你喜欢
        • 2015-05-15
        • 1970-01-01
        • 1970-01-01
        • 2013-06-26
        • 2013-04-16
        • 1970-01-01
        • 2013-10-06
        • 2013-03-15
        相关资源
        最近更新 更多