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 +

'}';

}

}

}

相关推荐
这里有鱼汤24 分钟前
全网最通俗易懂的趋势判断神器:卡尔曼滤波原来这么实用!
后端·python·程序员
二闹32 分钟前
告别面条儿代码!这个设计模式让逻辑清晰10倍!
后端·设计模式
这里有鱼汤35 分钟前
年化96%的小市值策略的选股逻辑源码来了
后端·python
知了一笑38 分钟前
独立开发:这才过去一个月?
人工智能·后端·程序员
逸风尊者1 小时前
开发也能看懂的大模型:聚类
人工智能·后端·trae
小李是个程序1 小时前
分层解耦(Controller,Service,Dao)
java·开发语言·spring boot·后端
Victor3562 小时前
MySQL(172)如何进行MySQL的全局变量设置?
后端
Victor3562 小时前
MySQL(171)如何使用MySQL的INFORMATION_SCHEMA?
后端
weixin_491853313 小时前
SpringBoot 实现 RAS+AES 自动接口解密
java·spring boot·后端
水上冰石3 小时前
【源力觉醒 创作者计划】文心一言与deepseek集成springboot开发哪个更方便
spring boot·后端·文心一言