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 +

'}';

}

}

}

相关推荐
神奇的程序员5 小时前
从已损坏的备份中拯救数据
运维·后端·前端工程化
oden6 小时前
AI服务商切换太麻烦?一个AI Gateway搞定监控、缓存和故障转移(成本降40%)
后端·openai·api
李慕婉学姐7 小时前
【开题答辩过程】以《基于Android的出租车运行监测系统设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·后端·vue
m0_740043737 小时前
SpringBoot05-配置文件-热加载/日志框架slf4j/接口文档工具Swagger/Knife4j
java·spring boot·后端·log4j
招风的黑耳8 小时前
我用SpringBoot撸了一个智慧水务监控平台
java·spring boot·后端
Miss_Chenzr8 小时前
Springboot优卖电商系统s7zmj(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
期待のcode8 小时前
Springboot核心构建插件
java·spring boot·后端
2501_921649498 小时前
如何获取美股实时行情:Python 量化交易指南
开发语言·后端·python·websocket·金融
serendipity_hky9 小时前
【SpringCloud | 第5篇】Seata分布式事务
分布式·后端·spring·spring cloud·seata·openfeign
五阿哥永琪9 小时前
Spring Boot 中自定义线程池的正确使用姿势:定义、注入与最佳实践
spring boot·后端·python