android okhttp mockserver 接收上传文件后,解析上传的文件。

Http Server 文件下载与上传,form表单字节码解析_文件传输 字节码 表单-CSDN博客

package com.feng.server.http;

import com.sun.net.httpserver.Headers;

import java.io.IOException;

import java.nio.charset.StandardCharsets;

import java.util.ArrayList;

import java.util.LinkedList;

import java.util.List;

public class HttpRequestFormResolver {

private static final int PARAM_INDEX = "Content-Disposition: form-data; ".length();

public static List<ParamItem> resolveForm(Headers headers, byte[] body) throws IOException {

String contentType = headers.getFirst("Content-type");

String boundary = contentType.substring(contentType.indexOf("=") + 1);

boundary = "--" + boundary; //size 52

byte[] boundaryBytes = boundary.getBytes(StandardCharsets.UTF_8);

List<ParamItem> paramItems = new LinkedList<>();

List<Integer> boundaryIndex = boundaryIndex(body, boundaryBytes);

int paramSize = boundaryIndex.size() - 1;

int boundaryLen = boundaryBytes.length;

byte[] sep = "\r\n".getBytes(StandardCharsets.UTF_8);

for (int i = 0; i < paramSize; i++) {

ParamItem paramItem = resolveParam(body, boundaryIndex.get(i)+boundaryLen+2, boundaryIndex.get(i+1), sep);

paramItems.add(paramItem);

}

return paramItems;

}

/**

* 单个参数解析

*/

private static ParamItem resolveParam(byte[] body, int start, int end, byte[] sep) {

int count = 0;

int cursor = start;

ParamItem paramItem = null;

for (int i = start; i < end; i++) {

for (int j = 0; j < 2; j++) {

if (body[i + j] == sep[j]) {

count++;

} else {

break;

}

}

if (count == 2) {

byte[] line = new byte[i-cursor];

System.arraycopy(body, cursor, line, 0,i-cursor);

//参数的key value分隔符 \r\n

if (isLineBlank(line)) {

cursor = i+2;

if (paramItem == null) {

return null;

}

if (paramItem.getType().equals("text")) {

byte[] val = new byte[end - cursor-2];

System.arraycopy(body, cursor, val, 0, end-cursor-2);

paramItem.setVal(new String(val));

} else {

paramItem.setStartIndex(cursor);

paramItem.setEndIndex(end);

}

break;

} else {

String lineStr = new String(line);

if (lineStr.startsWith("Content-Disposition: form-data; ")) {

paramItem = resolveParam(lineStr);

}

cursor = i;

}

i++;

}

count = 0;

}

return paramItem;

}

private static ParamItem resolveParam(String lineStr){

lineStr = lineStr.substring(PARAM_INDEX);

String[] kVs = lineStr.split(";");

ParamItem paramItem = new ParamItem();

paramItem.setType("text");

for (String kV : kVs) {

String[] k_v = kV.trim().split("=");

if ("name".equals(k_v[0])) {

paramItem.setName(k_v[1].replace("\"", ""));

} else if ("filename".equals(k_v[0])) {

paramItem.setFilename(k_v[1].replace("\"", ""));

paramItem.setType("file");

}

}

return paramItem;

}

private static boolean isLineBlank(byte[] line){

if (line.length == 0) {

return true;

}

byte[] sep = "\r\n".getBytes(StandardCharsets.UTF_8);

if (line.length == 2) {

if (line[0] == sep[0] && line[1] == sep[1]) {

return true;

}

}

return false;

}

/**

* 索引参数的index

*/

private static List<Integer> boundaryIndex(byte[] body, byte[] boundary){

int count = 0;

List<Integer> list = new ArrayList<>();

int length = body.length;

int boundaryLen = boundary.length;

for (int i = 0; i < length; i++) {

for (int j = 0; j < boundaryLen; j++) {

if (i + j == length) {

return list;

}

if (body[i + j] == boundary[j]) {

count++;

} else {

break;

}

}

if (count == boundaryLen) {

list.add(i);

i += boundaryLen - 1;

}

count = 0;

}

return list;

}

public static class ParamItem {

private String type;//text file

private String name;

private String filename;

private String val;

private int startIndex;

private int endIndex;

public String getType() {

return type;

}

public void setType(String type) {

this.type = type;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getFilename() {

return filename;

}

public void setFilename(String filename) {

this.filename = filename;

}

public String getVal() {

return val;

}

public void setVal(String val) {

this.val = val;

}

public int getStartIndex() {

return startIndex;

}

public void setStartIndex(int startIndex) {

this.startIndex = startIndex;

}

public int getEndIndex() {

return endIndex;

}

public void setEndIndex(int endIndex) {

this.endIndex = endIndex;

}

@Override

public String toString() {

return "ParamItem{" +

"type='" + type + '\'' +

", name='" + name + '\'' +

", filename='" + filename + '\'' +

", val='" + val + '\'' +

", startIndex=" + startIndex +

", endIndex=" + endIndex +

'}';

}

}

}

相关推荐
驰羽6 小时前
[GO]什么是热重载,如何使用Air工具
开发语言·后端·golang
SirLancelot16 小时前
MongoDB-基本介绍(一)基本概念、特点、适用场景、技术选型
java·数据库·分布式·后端·mongodb·软件工程·软件构建
程序员小凯7 小时前
Spring Boot消息队列与事件驱动详解
java·spring boot·后端
BUG弄潮儿7 小时前
go-swagger标准接口暴露
开发语言·后端·golang
阑梦清川7 小时前
linux系统jdk&&mysql配置
后端
i学长的猫8 小时前
Spring Boot 布隆过滤器最佳实践指南
spring boot·后端·哈希算法
Mr_hwt_1238 小时前
spring boot框架中本地缓存@Cacheable原理与踩坑点详细解析
java·spring boot·后端·缓存
qq_339191148 小时前
go win安装grpc-gen-go插件
开发语言·后端·golang
iCoding919 小时前
前端分页 vs 后端分页:技术选型
前端·后端·系统架构
王中阳Go背后的男人9 小时前
我发现不管是Java还是Golang,懂AI之后,是真吃香!
后端