在 Android 应用开发中,获取文件的扩展名是一个常见的需求。无论是用于文件管理、下载处理还是内容预览,准确地获取文件扩展名都至关重要。本文将详细介绍如何实现一个 FileExtensionFetcher
类,利用 HTTP 请求和 MIME 类型映射来获取文件扩展名,并分析其实际应用、性能开销及其他相关方面。
FileExtensionFetcher
类提供了通过 URL 获取文件扩展名的功能。该类通过 HTTP HEAD
请求获取文件的 MIME 类型,然后使用预定义的 MIME 类型映射表来确定文件的扩展名。如果无法从 MIME 类型中获取扩展名,它会尝试从 URL 路径中提取扩展名。该类使用后台线程处理网络请求,避免主线程阻塞,从而提升应用性能和响应速度。
ExecutorService
在后台线程中执行网络请求,确保主线程不被阻塞。IOException
和无效 MIME 类型,确保应用的健壮性。FileExtensionFetcher
使用 HashMap
将常见的 MIME 类型映射到文件扩展名。以下是一些常见的 MIME 类型及其对应的扩展名:
文档类型:
application/pdf
-> .pdf
text/plain
-> .txt
application/zip
-> .zip
图片类型:
image/jpeg
-> .jpg
image/png
-> .png
image/gif
-> .gif
image/webp
-> .webp
视频类型:
video/mp4
-> .mp4
video/x-matroska
-> .mkv
video/x-msvideo
-> .avi
video/quicktime
-> .mov
video/x-flv
-> .flv
video/webm
-> .webm
音频类型:
audio/mpeg
-> .mp3
audio/wav
-> .wav
audio/ogg
-> .ogg
audio/mp4
-> .m4a
audio/flac
-> .flac
FileExtensionFetcher
类实现import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; public class FileExtensionFetcher { private static final Map MIME_TYPE_MAP = new HashMap<>(); static { MIME_TYPE_MAP.put("application/pdf", ".pdf"); MIME_TYPE_MAP.put("text/plain", ".txt"); MIME_TYPE_MAP.put("application/zip", ".zip"); MIME_TYPE_MAP.put("image/jpeg", ".jpg"); MIME_TYPE_MAP.put("image/png", ".png"); MIME_TYPE_MAP.put("image/gif", ".gif"); MIME_TYPE_MAP.put("image/webp", ".webp"); MIME_TYPE_MAP.put("video/mp4", ".mp4"); MIME_TYPE_MAP.put("video/x-matroska", ".mkv"); MIME_TYPE_MAP.put("video/x-msvideo", ".avi"); MIME_TYPE_MAP.put("video/quicktime", ".mov"); MIME_TYPE_MAP.put("video/x-flv", ".flv"); MIME_TYPE_MAP.put("video/webm", ".webm"); MIME_TYPE_MAP.put("audio/mpeg", ".mp3"); MIME_TYPE_MAP.put("audio/wav", ".wav"); MIME_TYPE_MAP.put("audio/ogg", ".ogg"); MIME_TYPE_MAP.put("audio/mp4", ".m4a"); MIME_TYPE_MAP.put("audio/flac", ".flac"); } private static final ExecutorService executor = Executors.newSingleThreadExecutor(); public static void fetchFileExtension(String fileUrl, Callback callback) { executor.submit(() -> { String extension = getFileExtension(fileUrl); callback.onResult(extension); }); } private static String getFileExtension(String fileUrl) { HttpURLConnection connection = null; try { URL url = new URL(fileUrl); connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("HEAD"); String contentType = connection.getHeaderField("Content-Type"); if (contentType != null) { // 处理 MIME 类型可能包含字符集的情况 String baseType = contentType.split(";")[0]; String extension = MIME_TYPE_MAP.get(baseType); if (extension != null) { return extension; } } // 如果 MIME 类型是 application/octet-stream 或者没有 MIME 类型 if (contentType != null && contentType.startsWith("application/octet-stream")) { // 尝试从 Content-Disposition 头中获取文件名 String contentDisposition = connection.getHeaderField("Content-Disposition"); if (contentDisposition != null) { String filename = parseFileNameFromContentDisposition(contentDisposition); if (filename != null && filename.contains(".")) { return filename.substring(filename.lastIndexOf('.')); } } } // 从 URL 路径中提取扩展名 String path = url.getPath(); if (path != null && path.contains(".")) { return path.substring(path.lastIndexOf('.')); } // 如果无法获取扩展名,则返回默认扩展名 return ".bin"; } catch (IOException e) { e.printStackTrace(); return null; } finally { if (connection != null) { connection.disconnect(); } } } private static String parseFileNameFromContentDisposition(String contentDisposition) { String[] parts = contentDisposition.split(";"); for (String part : parts) { String[] nameValue = part.trim().split("="); if (nameValue.length == 2 && "filename".equalsIgnoreCase(nameValue[0].trim())) { return nameValue[1].trim().replace("\"", ""); } } return null; } public interface Callback { void onResult(String extension); } }
FileExtensionFetcher.fetchFileExtension("https://example.com/video.mp4", new FileExtensionFetcher.Callback() { @Override public void onResult(String extension) { // 在主线程中更新 UI 或执行其他操作 System.out.println("File extension: " + extension); } });
网络请求:
HEAD
请求:通常比 GET
请求更快,但耗时依赖于网络状况和服务器响应。实际响应时间从几百毫秒到几秒钟不等。URL 解析:
线程池管理:
ExecutorService
处理请求,开销较小,适用于少量请求。对于高并发场景,可以调整线程池配置或使用多线程池。错误处理:
性能优化:
安全性:
FileExtensionFetcher
类通过灵活的 MIME 类型映射和异步处理,实现了从 URL 获取文件扩展名的功能。其实现考虑了 MIME 类型的多样性和网络请求的性能,适用于多种应用场景。在实际使用中,可以根据需求进一步优化和扩展。