Android使用NanoHTTPD上传和下载文件
本文参考:A Guide to NanoHTTPD
NanoHTTPD has a separate dependency for file uploads, so let's add it to our project:
<dependency>
<groupId>org.nanohttpd</groupId>
<artifactId>nanohttpd-apache-fileupload</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>Please note that the servlet-api dependency is also needed (otherwise we'll get a compilation error).
What NanoHTTPD exposes is a class called NanoFileUpload:
@Override
public Response serve(IHTTPSession session) {
try {
List<FileItem> files
= new NanoFileUpload(new DiskFileItemFactory()).parseRequest(session);
int uploadedCount = 0;
for (FileItem file : files) {
try {
String fileName = file.getName();
byte[] fileContent = file.get();
Files.write(Paths.get(fileName), fileContent);
uploadedCount++;
} catch (Exception exception) {
// handle
}
}
return newFixedLengthResponse(Response.Status.OK, MIME_PLAINTEXT,
"Uploaded files " + uploadedCount + " out of " + files.size());
} catch (IOException | FileUploadException e) {
throw new IllegalArgumentException("Could not handle files from API request", e);
}
return newFixedLengthResponse(
Response.Status.BAD_REQUEST, MIME_PLAINTEXT, "Error when uploading");
}Hey, let's try it out:
> curl -F 'filename=@/pathToFile.txt' 'http://localhost:8080'
Uploaded files: 1以上是在 PC 上的方式,现在改成 Android 方式。
在 build.gradle 中添加依赖:
implementation 'org.nanohttpd:nanohttpd:2.3.1'
implementation 'org.nanohttpd:nanohttpd-apache-fileupload:2.3.1'
implementation 'javax.servlet:javax.servlet-api:4.0.1'
implementation 'com.blankj:utilcodex:1.31.0'创建 App 类:
public class App extends NanoHTTPD {
public App(int port) throws IOException {
super(8080);
start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
}
@Override
public Response serve(IHTTPSession session) {
try {
List<FileItem> files
= new NanoFileUpload(new DiskFileItemFactory()).parseRequest(session);
int uploadedCount = 0;
for(FileItem file : files) {
try {
String fileName = file.getName();
byte[] fileContent = file.get();
FileIOUtils.writeFileFromBytesByStream(PathUtils.getExternalDownloadsPath() + "/" + fileName,fileContent);
uploadedCount++;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return newFixedLengthResponse(Response.Status.OK, MIME_PLAINTEXT,
"Uploaded files " + uploadedCount + " out of " + files.size());
} catch (FileUploadException e) {
throw new IllegalArgumentException("Could not handle files from API request", e);
}
}
}FileItem 是一个接口,提供 get() 方法,将文件内容输出为字节组,同时也提供 getInputStream() 和 getOutputStream() 来提供字节流,更多细节参考官方文档:Interface FileItem
FileIOUtils 是一个 Android 第三方库提供的工具类(AndroidUtilCode),writeFileFromBytesByStream() 方法将字节数组写入到对应的文件。
PathUtils 同样是 AndroidUtilCode 提供的针对 Android 文件夹路径的工具类,这里就不详细叙述了。
测试:
curl -F 'filename=@./e42ef5d7-3a30-4777-a988-ed561a73e2d9.xlsx' 'http://192.168.8.180:8080'在 Android 的内部存储中的 Download 目录下即可看到上传的文件,另外如果文件名包含中文则是上传失败的。
下载文件
参考:How to download two or multiple files at a time in Android using NanoHTTPD?
这里对于下载文件是 GET 请求,而之前上传文件用的是 POST 请求。
在 public Response serve(IHTTPSession session)中添加对 GET 请求下载文件的处理:
// HTTP GET
if (session.getMethod() == Method.GET) {
// 要下载的文件名称
String fileName = Objects.requireNonNull(session.getParameters().get("download")).get(0);
Log.d("fileName",fileName);
if(!fileName.isEmpty()) {
File downloadFile;
downloadFile = new File(PathUtils.getExternalDownloadsPath() + "/" + fileName);
return downloadFile(downloadFile);
}
}下载文件的请求命名为 curl 'http://ip:port/?download=e42ef5d7-3a30-4777-a988-ed561a73e2d9.xlsx',使用 download= 的方式来指定要下载的文件名。
创建下载文件函数 downloadFile()
/**
* 下载文件
* @param file 文件对象
* @return 文件流
*/
private Response downloadFile(File file)
{
FileInputStream fis = null;
try
{
fis = new FileInputStream(file);
} catch (FileNotFoundException ex)
{
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
}
return newFixedLengthResponse(Response.Status.OK, "application/octet-stream", fis, file.length());
}上传下载完整代码
import android.util.Log;
import com.blankj.utilcode.util.FileIOUtils;
import com.blankj.utilcode.util.PathUtils;
import fi.iki.elonen.NanoFileUpload;
import fi.iki.elonen.NanoHTTPD;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
public class App extends NanoHTTPD {
public App(int port) throws IOException {
super(8080);
start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
}
@Override
public Response serve(IHTTPSession session) {
// HTTP GET
if (session.getMethod() == Method.GET) {
// 要下载的文件名称
String fileName = Objects.requireNonNull(session.getParameters().get("download")).get(0);
Log.d("fileName",fileName);
if(!fileName.isEmpty()) {
File downloadFile;
downloadFile = new File(PathUtils.getExternalDownloadsPath() + "/" + fileName);
return downloadFile(downloadFile);
}
}
// HTTP POST
if (session.getMethod() == Method.POST) {
// 判断是否是上传文件的 POST 请求
if(Objects.requireNonNull(session.getHeaders().get("content-type")).split(";")[0].equals("multipart/form-data")) {
// 处理 POST 方式的文件上传
try {
List<FileItem> files
= new NanoFileUpload(new DiskFileItemFactory()).parseRequest(session);
int uploadedCount = 0;
for(FileItem file : files) {
try {
String fileName = file.getName();
byte[] fileContent = file.get();
FileIOUtils.writeFileFromBytesByStream(PathUtils.getExternalDownloadsPath() + "/" + fileName,fileContent);
uploadedCount++;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return newFixedLengthResponse(Response.Status.OK, MIME_PLAINTEXT,
"Uploaded files " + uploadedCount + " out of " + files.size());
} catch (FileUploadException e) {
throw new IllegalArgumentException("Could not handle files from API request", e);
}
}
// 处理非文件上传类型的 POST
Map<String, String> parms = new HashMap<String, String>();
try {
session.parseBody(parms);
String requestBody = session.getQueryParameterString();
return newFixedLengthResponse("Request body = " + requestBody);
} catch (IOException | ResponseException e) {
// handle
}
}
return newFixedLengthResponse(Response.Status.NOT_FOUND, MIME_PLAINTEXT,
"The requested resource does not exist");
}
/**
* 下载文件
* @param file 文件对象
* @return 文件流
*/
private Response downloadFile(File file)
{
FileInputStream fis = null;
try
{
fis = new FileInputStream(file);
} catch (FileNotFoundException ex)
{
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
}
return newFixedLengthResponse(Response.Status.OK, "application/octet-stream", fis, file.length());
}
}
评论已关闭