普通Java类获取context

场景:存储数据使用的是SharedPreferences,都知道最简单的获得SharedPreferences的方法是context.getSharedPreferences,但是我存储数据的环节放在了自定义Interceptor(okhttp3)的interceptor方法里面,这个类是无法从Activity/Fragment里获取context的,当然,可以设置一个context的属性放在构造函数或者使用set方法,在使用该类的时候传入context。
这样做的一个弊端在于,如果项目里面对网络请求方法进行了深度的封装,那么context就要存在于一个链上,尽管它并不需要在其他类里使用。
那么换个思路,Android里还有ApplicationContext可以获取。
但是默认的Application类在普通Java类里也是无法得到的,因此需要自定义一个MainApplication(名称随意)

public class MainApplication extends Application {
	private static Context mContext;
	
	@Override
    public void onCreate() {
        super.onCreate();
        mContext = getApplicationContext();
    }

    public static Context getCustomApplicationContext() {
        return mContext;
    }
}

别忘了在AndroidManifest.xml里修改

<application
	android:name="MainApplication"
>

这样自定义的Application才能生效,之后需要获取context的话,可以通过MainApplication.getCustomApplicationContext()方法来获取。

<think> 我们面对的问题是:在普通Java中(非Servlet环境),如何使用已获取的ByteArrayInputStream实现浏览器下载文件并弹出保存框。 注意:浏览器下载文件并弹出保存框通常是通过HTTP响应实现的,而HTTP响应通常是在Web环境中(如Servlet)完成的。在非Servlet环境中(如Swing桌面应用、命令行程序等),我们通常不直接与浏览器交互。 但是,如果我们是在一个非Web环境的Java应用中,想要调用系统默认浏览器来下载文件并弹出保存框,那么我们可以通过以下思路: 1. 将ByteArrayInputStream保存为临时文件。 2. 使用Java的Desktop打开浏览器,并将临时文件的URL传递给浏览器,但是这种方法并不能直接控制浏览器弹出保存框,因为浏览器会根据响应头来决定是显示还是下载。 然而,如果我们能够启动一个简单的HTTP服务器(例如,使用内嵌的HTTP服务器),那么我们就可以模拟Web环境,然后通过浏览器访问该服务器上的一个URL,该URL对应的处理会设置正确的响应头,从而触发下载。 所以,在非Servlet环境中,我们可以这样做: 步骤1:创建一个内嵌的HTTP服务器(例如使用JDK自带的HttpServer或第三方库如Simple Framework)。 步骤2:将ByteArrayInputStream的内容通过一个HTTP端点提供,并设置正确的响应头(Content-Disposition等)。 步骤3:生成一个URL,并使用Desktop.getDesktop().browse(uri)打开浏览器访问该URL,从而触发下载。 下面是一个示例实现: 1. 使用JDK内置的HttpServer(注意:最小化实现,不考虑多线程和性能优化): 2. 在服务器上创建一个上下文(context)来处理下载请求。 3. 在处理器中设置响应头,并将ByteArrayInputStream写入输出流。 4. 启动服务器并打开浏览器。 示例代码: ```java import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; import java.awt.*; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.URI; import java.net.URLEncoder; public class DownloadUtil { public static void downloadInBrowser(byte[] fileBytes, String fileName) throws Exception { // 创建一个临时HTTP服务器,端口随机 HttpServer server = HttpServer.create(new InetSocketAddress(0), 0); // 设置上下文处理器 server.createContext("/download", new DownloadHandler(fileBytes, fileName)); server.start(); // 获取服务器实际绑定的端口 int port = server.getAddress().getPort(); String url = "http://localhost:" + port + "/download"; // 使用默认浏览器打开 openBrowser(url); // 注意:在实际应用中,我们可能需要等待下载完成后再关闭服务器,但这里为了简单,我们暂时不关闭。 // 可以设计一个机制,比如在下载完成后关闭服务器(例如,通过一个延迟任务或者在处理器中统计下载完成后关闭) } private static void openBrowser(String url) throws IOException { if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { Desktop.getDesktop().browse(URI.create(url)); } else { throw new UnsupportedOperationException("Desktop browsing not supported"); } } static class DownloadHandler implements HttpHandler { private final byte[] fileBytes; private final String fileName; public DownloadHandler(byte[] fileBytes, String fileName) { this.fileBytes = fileBytes; this.fileName = fileName; } @Override public void handle(HttpExchange exchange) throws IOException { try { // 设置响应头,触发下载 String encodedFileName = URLEncoder.encode(fileName, "UTF-8").replace("+", "%20"); exchange.getResponseHeaders().set("Content-Disposition", "attachment; filename=" + encodedFileName); exchange.getResponseHeaders().set("Content-Type", "application/octet-stream"); exchange.sendResponseHeaders(200, fileBytes.length); // 写入文件数据 try (OutputStream os = exchange.getResponseBody(); ByteArrayInputStream bis = new ByteArrayInputStream(fileBytes)) { byte[] buffer = new byte[8192]; int count; while ((count = bis.read(buffer)) != -1) { os.write(buffer, 0, count); } } } finally { exchange.close(); // 这里可以加入关闭服务器的逻辑,但注意:这个处理器每次处理一个请求,如果希望一次下载后关闭服务器,可以在这里关闭。 // 但是注意,关闭服务器需要访问到server对象,这里没有传递,所以我们可以设计成在最后一次下载后关闭(例如,使用单次下载服务器) } } } public static void main(String[] args) throws Exception { // 示例:下载一个文本文件 String content = "这是一个测试文件内容"; downloadInBrowser(content.getBytes("UTF-8"), "测试文件.txt"); } } ``` 注意: 1. 这个示例中,我们使用了`com.sun.net.httpserver.HttpServer`,它是JDK自带的,但属于非公开API(虽然通常可用)。 2. 我们使用随机端口(0表示由系统分配)以避免端口冲突。 3. 在打开浏览器后,服务器会一直运行,直到程序退出。在实际应用中,你可能希望下载完成后自动关闭服务器。可以通过在处理器中记录下载次数并在一次下载后关闭服务器(注意:需要将server对象传给处理器,或者使用其他方式)。 另一种简化做法:我们可以在处理器中关闭服务器(但要确保在响应发送完毕后再关闭)。例如: 修改DownloadHandler,增加server参数: ```java // 在DownloadHandler中添加 private final HttpServer server; // 修改构造方法 public DownloadHandler(byte[] fileBytes, String fileName, HttpServer server) { this.fileBytes = fileBytes; this.fileName = fileName; this.server = server; } // 在finally块中关闭服务器(注意:只关闭一次,可以用AtomicBoolean等控制) finally { exchange.close(); // 延迟关闭,确保响应已经发送 server.stop(0); // 0表示立即停止 } ``` 然后在创建处理器时传入server对象。 但是,请注意,关闭服务器后,端口会被释放,但浏览器可能还在下载(实际上数据已经发送完毕,所以一般没有问题)。 另外,如果不想使用内嵌HTTP服务器,也可以考虑以下方法: - 将文件保存到本地,然后使用`Desktop.getDesktop().open(File)`打开文件所在目录,但这并不会触发下载。 - 或者,生成一个HTML文件,其中包含一个自动点击的下载链接(使用data URL),然后让浏览器打开这个HTML文件。但是,data URL有长度限制(通常浏览器限制为几MB),所以不适合大文件。 使用data URL的示例: ```java public static void downloadViaDataURL(byte[] fileBytes, String fileName) throws IOException { String base64 = Base64.getEncoder().encodeToString(fileBytes); String mimeType = "application/octet-stream"; // 或者根据文件型设置 String url = "data:" + mimeType + ";base64," + base64; // 生成一个HTML文件,通过自动点击a标签触发下载 String html = "<!DOCTYPE html><html><body><script>" + "var a = document.createElement('a');" + "a.href = '" + url + "';" + "a.download = '" + fileName.replace("'", "\\'") + "';" + "document.body.appendChild(a);" + "a.click();" + "setTimeout(function() { window.close(); }, 1000);" // 延迟关闭窗口 + "</script></body></html>"; File tempFile = File.createTempFile("download", ".html"); try (FileWriter writer = new FileWriter(tempFile)) { writer.write(html); } Desktop.getDesktop().browse(tempFile.toURI()); } ``` 但是,这种方法有局限性: - 文件大小受限(base64编码后体积增加约33%,且浏览器对data URL长度有限制)。 - 文件名可能在某些浏览器中无法正确设置。 因此,对于小文件,可以使用data URL方法;但是对于大文件,建议使用内嵌HTTP服务器的方法。 综上所述,在非Servlet环境下,推荐使用内嵌HTTP服务器的方法来实现浏览器下载并弹出保存框。 §§相关问题§§ 1. 除了HttpServer,还有哪些轻量级的Java HTTP服务器库? 2. 如何确保临时HTTP服务器在下载完成后自动关闭? 3. 使用data URL下载文件有哪些优缺点? </think> ### 在普通Java中使用ByteArrayInputStream实现浏览器下载文件(非Servlet写法) 在普通Java中实现浏览器下载并弹出保存框,核心思路是通过生成包含文件数据的HTML页面,利用Blob对象和URL.createObjectURL实现下载。以下是完整实现方案: #### 完整实现代码 ```java import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; import java.util.Base64; public class FileDownloader { public static void triggerDownload(byte[] fileBytes, String fileName) { // 1. 将文件数据转换为Base64编码 String base64Data = Base64.getEncoder().encodeToString(fileBytes); // 2. 构建包含Blob下载逻辑的HTML页面 String html = buildDownloadHtml(base64Data, fileName); // 3. 在浏览器中打开生成的HTML openInBrowser(html); } private static String buildDownloadHtml(String base64Data, String fileName) { return "<!DOCTYPE html>" + "<html>" + "<head>" + " <script>" + " function download() {" + " const byteCharacters = atob('" + base64Data + "');" + " const byteNumbers = new Array(byteCharacters.length);" + " for (let i = 0; i < byteCharacters.length; i++) {" + " byteNumbers[i] = byteCharacters.charCodeAt(i);" + " }" + " const byteArray = new Uint8Array(byteNumbers);" + " const blob = new Blob([byteArray], {type: 'application/octet-stream'});" + " const url = URL.createObjectURL(blob);" + " const a = document.createElement('a');" + " a.href = url;" + " a.download = '" + fileName + "';" + " document.body.appendChild(a);" + " a.click();" + " setTimeout(() => {" + " document.body.removeChild(a);" + " window.URL.revokeObjectURL(url);" + " }, 100);" + " }" + " </script>" + "</head>" + "<body onload='download()'>" + " <h3>文件下载中...</h3>" + "</body>" + "</html>"; } private static void openInBrowser(String htmlContent) { try { // 创建临时HTML文件 java.nio.file.Path tempFile = java.nio.file.Files.createTempFile("download", ".html"); java.nio.file.Files.write(tempFile, htmlContent.getBytes(StandardCharsets.UTF_8)); // 在默认浏览器中打开 java.awt.Desktop.getDesktop().browse(tempFile.toUri()); } catch (Exception e) { throw new RuntimeException("无法打开浏览器: " + e.getMessage()); } } // 使用示例 public static void main(String[] args) { String content = "文件内容样例"; triggerDownload(content.getBytes(StandardCharsets.UTF_8), "示例文件.txt"); } } ``` #### 核心原理说明 1. **数据转换** 使用Base64编码将`ByteArrayInputStream`的数据转换为字符串: $$ \text{base64Data} = \text{Base64.getEncoder().encodeToString(fileBytes)} $$ 2. **Blob对象创建** 在HTML中使用JavaScript将Base64数据转换为二进制Blob对象: ```javascript const blob = new Blob([byteArray], {type: 'application/octet-stream'}) ``` 3. **模拟点击下载** 动态创建隐藏的`<a>`标签并触发点击事件: ```javascript a.download = '文件名.txt'; // 设置保存时的默认文件名 a.click(); // 触发浏览器下载对话框 ``` #### 关键优势 1. **无需Servlet环境** 完全在普通Java中实现,不依赖Web容器或Servlet API 2. **跨浏览器兼容** 使用标准Blob API,支持现代浏览器(Chrome/Firefox/Edge) 3. **自动触发保存框** 通过`a.download`属性确保浏览器弹出保存对话框 4. **资源清理** 使用`URL.revokeObjectURL()`释放Blob对象内存[^1] #### 注意事项 1. **文件大小限制** 浏览器对单个Blob对象有大小限制(通常约500MB),超大文件需分块处理 2. **文件名编码** 文件名含特殊字符时需JavaScript编码处理: ```javascript a.download = encodeURIComponent(fileName) ``` 3. **数据型指定** 根据实际文件型设置MIME型,如: - PDF: `application/pdf` - Excel: `application/vnd.ms-excel` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值