This commit is contained in:
Redkale
2017-05-14 15:52:15 +08:00
parent 9954eaf469
commit ef28e32e04
6 changed files with 295 additions and 7 deletions

View File

@@ -5,6 +5,7 @@
*/
package org.redkale.convert;
import java.io.File;
import java.lang.reflect.*;
import java.math.BigInteger;
import java.net.*;
@@ -91,6 +92,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
this.register(Class.class, TypeSimpledCoder.instance);
this.register(InetSocketAddress.class, InetSocketAddressSimpledCoder.instance);
this.register(Pattern.class, PatternSimpledCoder.instance);
this.register(File.class, FileSimpledCoder.instance);
this.register(CompletionHandler.class, CompletionHandlerSimpledCoder.instance);
this.register(AsyncHandler.class, AsyncHandlerSimpledCoder.instance);
this.register(URL.class, URLSimpledCoder.instance);

View File

@@ -0,0 +1,41 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert.ext;
import java.io.File;
import org.redkale.convert.*;
/**
* 文件 的SimpledCoder实现
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <R> Reader输入的子类型
* @param <W> Writer输出的子类型
*/
public class FileSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, File> {
public static final PatternSimpledCoder instance = new PatternSimpledCoder();
@Override
public void convertTo(W out, File value) {
if (value == null) {
out.writeNull();
} else {
out.writeString(value.getPath());
}
}
@Override
public File convertFrom(R in) {
String value = in.readString();
if (value == null) return null;
return new File(value);
}
}

View File

@@ -87,10 +87,103 @@ public final class MultiContext {
return this.boundary != null;
}
//或被 REST 用到
/**
* 获取第一个文件的二进制
*
* @param max 可接收的文件大小最大值
* @param filenameReg 可接收的文件名正则表达式
* @param contentTypeReg 可接收的ContentType正则表达式
*
* @return 二进制文件
* @throws IOException
*/
public byte[] partsFirstBytes(final long max, final String filenameReg, final String contentTypeReg) throws IOException {
if (!isMultipart()) return null;
for (MultiPart part : parts()) {
if (filenameReg != null && !filenameReg.isEmpty() && !part.getFilename().matches(filenameReg)) return null;
if (contentTypeReg != null && !contentTypeReg.isEmpty() && !part.getContentType().matches(contentTypeReg)) return null;
return part.getContentBytes(max < 1 ? Long.MAX_VALUE : max);
}
return null;
}
//或被 REST 用到
/**
* 获取第一个文件
*
* @param home 进程目录
* @param max 可接收的文件大小最大值
* @param filenameReg 可接收的文件名正则表达式
* @param contentTypeReg 可接收的ContentType正则表达式
*
* @return 文件
* @throws IOException
*/
public File partsFirstFile(final File home, final long max, final String filenameReg, final String contentTypeReg) throws IOException {
if (!isMultipart()) return null;
for (MultiPart part : parts()) {
if (filenameReg != null && !filenameReg.isEmpty() && !part.getFilename().matches(filenameReg)) return null;
if (contentTypeReg != null && !contentTypeReg.isEmpty() && !part.getContentType().matches(contentTypeReg)) return null;
String name = part.getFilename();
int pos = name.lastIndexOf('.');
if (pos > 0) {
int pos2 = name.lastIndexOf('.', pos - 1);
if (pos2 >= 0) pos = pos2;
}
File file = new File(home, "tmp/redkale_" + System.nanoTime() + (pos > 0 ? name.substring(pos) : name));
file.getParentFile().mkdirs();
boolean rs = part.save(max < 1 ? Long.MAX_VALUE : max, file);
if (!rs) {
file.delete();
return null;
}
return file;
}
return null;
}
//或被 REST 用到
/**
* 获取所有文件
*
* @param home 进程目录
* @param max 可接收的文件大小最大值
* @param filenameReg 可接收的文件名正则表达式
* @param contentTypeReg 可接收的ContentType正则表达式
*
* @return 文件列表
* @throws IOException
*/
public File[] partsFiles(final File home, final long max, final String filenameReg, final String contentTypeReg) throws IOException {
if (!isMultipart()) return null;
List<File> files = null;
for (MultiPart part : parts()) {
if (filenameReg != null && !filenameReg.isEmpty() && !part.getFilename().matches(filenameReg)) continue;
if (contentTypeReg != null && !contentTypeReg.isEmpty() && !part.getContentType().matches(contentTypeReg)) continue;
String name = part.getFilename();
int pos = name.lastIndexOf('.');
if (pos > 0) {
int pos2 = name.lastIndexOf('.', pos - 1);
if (pos2 >= 0) pos = pos2;
}
File file = new File(home, "tmp/redkale_" + System.nanoTime() + (pos > 0 ? name.substring(pos) : name));
file.getParentFile().mkdirs();
boolean rs = part.save(max < 1 ? Long.MAX_VALUE : max, file);
if (!rs) {
file.delete();
continue;
}
if (files == null) files = new ArrayList<>();
files.add(file);
}
return files == null ? null : files.toArray(new File[files.size()]);
}
/**
* 获取上传文件信息列表
*
* @return Iterable
* @return Iterable
* @throws IOException IOException
*/
public Iterable<MultiPart> parts() throws IOException {

View File

@@ -189,6 +189,13 @@ public final class Rest {
av0.visitEnd();
fv.visitEnd();
}
{ //注入 @Resource(name = "APP_HOME") private File _redkale_home;
fv = cw.visitField(ACC_PRIVATE, "_redkale_home", Type.getDescriptor(File.class), null, null);
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
av0.visit("name", "APP_HOME");
av0.visitEnd();
fv.visitEnd();
}
{ //_servicemap字段 Map<String, XXXService>
fv = cw.visitField(ACC_PRIVATE, REST_SERVICEMAP_FIELD_NAME, "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;" + serviceDesc + ">;", null);
fv.visitEnd();
@@ -254,6 +261,7 @@ public final class Rest {
final Map<String, java.lang.reflect.Type> bodyTypes = new HashMap<>();
for (final MappingEntry entry : entrys) {
boolean hasupload = false;
final Method method = entry.mappingMethod;
final Class returnType = method.getReturnType();
final java.lang.reflect.Type returnGenericType = method.getGenericReturnType();
@@ -340,6 +348,17 @@ public final class Rest {
if (annaddr != null) throw new RuntimeException("@RestBody and @RestAddress cannot on the same Parameter in " + method);
if (ptype.isPrimitive()) throw new RuntimeException("@RestBody cannot on primitive type Parameter in " + method);
}
RestUploadFile annfile = param.getAnnotation(RestUploadFile.class);
if (annfile != null) {
if (hasupload) throw new RuntimeException("@RestUploadFile repeat in " + method);
hasupload = true;
if (annhead != null) throw new RuntimeException("@RestUploadFile and @RestHeader cannot on the same Parameter in " + method);
if (anncookie != null) throw new RuntimeException("@RestUploadFile and @RestCookie cannot on the same Parameter in " + method);
if (annsid != null) throw new RuntimeException("@RestUploadFile and @RestSessionid cannot on the same Parameter in " + method);
if (annaddr != null) throw new RuntimeException("@RestUploadFile and @RestAddress cannot on the same Parameter in " + method);
if (annbody != null) throw new RuntimeException("@RestUploadFile and @RestBody cannot on the same Parameter in " + method);
if (ptype != byte[].class && ptype != File.class && ptype != File[].class) throw new RuntimeException("@RestUploadFile must on byte[] or File or File[] Parameter in " + method);
}
RestParam annpara = param.getAnnotation(RestParam.class);
if (annpara != null) radix = annpara.radix();
@@ -357,11 +376,11 @@ public final class Rest {
n = ("bean" + i);
}
}
if (annhead == null && anncookie == null && annaddr == null && annbody == null
if (annhead == null && anncookie == null && annaddr == null && annbody == null && annfile == null
&& (entry.name.startsWith("find") || entry.name.startsWith("delete")) && params.length == 1) {
if (ptype.isPrimitive() || ptype == String.class) n = "#";
}
paramlist.add(new Object[]{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody, param.getParameterizedType()});
paramlist.add(new Object[]{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody, annfile, param.getParameterizedType()});
}
Map<String, Object> mappingMap = new LinkedHashMap<>();
@@ -405,7 +424,7 @@ public final class Rest {
av0 = mv.visitAnnotation(webparamsDesc, true);
AnnotationVisitor av1 = av0.visitArray("value");
//设置 WebParam
for (Object[] ps : paramlist) { //{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody, pgentype}
for (Object[] ps : paramlist) { //{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody, annfile, pgentype}
final boolean ishead = ((RestHeader) ps[9]) != null; //是否取getHeader 而不是 getParameter
final boolean iscookie = ((RestCookie) ps[10]) != null; //是否取getCookie
@@ -439,7 +458,8 @@ public final class Rest {
RestHeader annhead = (RestHeader) ps[9];
RestCookie anncookie = (RestCookie) ps[10];
RestBody annbody = (RestBody) ps[11];
java.lang.reflect.Type pgentype = (java.lang.reflect.Type) ps[12];
RestUploadFile annfile = (RestUploadFile) ps[12];
java.lang.reflect.Type pgentype = (java.lang.reflect.Type) ps[13];
final boolean ishead = annhead != null; //是否取getHeader 而不是 getParameter
final boolean iscookie = anncookie != null; //是否取getCookie
@@ -495,6 +515,39 @@ public final class Rest {
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
}
} else if (annfile != null) { //MultiContext.partsFirstBytes / HttpRequest.partsFirstFile / HttpRequest.partsFiles
if (ptype == byte[].class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()Lorg/redkale/net/http/MultiContext;", false);
mv.visitLdcInsn(annfile.maxLength());
mv.visitLdcInsn(annfile.fileNameReg());
mv.visitLdcInsn(annfile.contentTypeReg());
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/net/http/MultiContext", "partsFirstBytes", "(JLjava/lang/String;Ljava/lang/String;)[B", false);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else if (ptype == File.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()Lorg/redkale/net/http/MultiContext;", false);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "_redkale_home", "Ljava/io/File;");
mv.visitLdcInsn(annfile.maxLength());
mv.visitLdcInsn(annfile.fileNameReg());
mv.visitLdcInsn(annfile.contentTypeReg());
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/net/http/MultiContext", "partsFirstFile", "(Ljava/io/File;JLjava/lang/String;Ljava/lang/String;)Ljava/io/File;", false);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else if (ptype == File[].class) { //File[]
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()Lorg/redkale/net/http/MultiContext;", false);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "_redkale_home", "Ljava/io/File;");
mv.visitLdcInsn(annfile.maxLength());
mv.visitLdcInsn(annfile.fileNameReg());
mv.visitLdcInsn(annfile.contentTypeReg());
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/net/http/MultiContext", "partsFiles", "(Ljava/io/File;JLjava/lang/String;Ljava/lang/String;)[Ljava/io/File;", false);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
}
} else if ("#".equals(pname)) { //从request.getRequstURI 中取参数
if (ptype == boolean.class) {
mv.visitVarInsn(ALOAD, 1);
@@ -772,12 +825,20 @@ public final class Rest {
RestSessionid rs = field.getAnnotation(RestSessionid.class);
RestAddress ra = field.getAnnotation(RestAddress.class);
RestBody rb = field.getAnnotation(RestBody.class);
if (rh == null && rc == null && ra == null && rb == null && rs == null) continue;
RestUploadFile ru = field.getAnnotation(RestUploadFile.class);
if (rh == null && rc == null && ra == null && rb == null && rs == null && ru == null) continue;
if (rh != null && field.getType() != String.class) throw new RuntimeException("@RestHeader must on String Field in " + field);
if (rc != null && field.getType() != String.class) throw new RuntimeException("@RestCookie must on String Field in " + field);
if (rs != null && field.getType() != String.class) throw new RuntimeException("@RestSessionid must on String Field in " + field);
if (ra != null && field.getType() != String.class) throw new RuntimeException("@RestAddress must on String Field in " + field);
if (rb != null && field.getType().isPrimitive()) throw new RuntimeException("@RestBody must on cannot on primitive type Field in " + field);
if (ru != null && field.getType() != byte[].class && field.getType() != File.class && field.getType() != File[].class) {
throw new RuntimeException("@RestUploadFile must on byte[] or File or File[] Field in " + field);
}
if (ru != null) {
if (hasupload) throw new RuntimeException("@RestUploadFile repeat on Field(" + field + ") in " + method);
hasupload = true;
}
org.redkale.util.Attribute attr = org.redkale.util.Attribute.create(loop, field);
String attrFieldName;
String restname = "";
@@ -802,11 +863,20 @@ public final class Rest {
} else if (rb != null && field.getType() != String.class && field.getType() != byte[].class) {
attrFieldName = "_redkale_attr_bodyjson_" + restAttributes.size();
//restname = "";
} else if (ru != null && field.getType() == byte[].class) {
attrFieldName = "_redkale_attr_uploadbytes_" + restAttributes.size();
//restname = "";
} else if (ru != null && field.getType() == File.class) {
attrFieldName = "_redkale_attr_uploadfile_" + restAttributes.size();
//restname = "";
} else if (ru != null && field.getType() == File[].class) {
attrFieldName = "_redkale_attr_uploadfiles_" + restAttributes.size();
//restname = "";
} else {
continue;
}
restAttributes.put(attrFieldName, attr);
attrParaNames.put(attrFieldName, new Object[]{restname, field.getType(), field.getGenericType()});
attrParaNames.put(attrFieldName, new Object[]{restname, field.getType(), field.getGenericType(), ru});
fields.add(field.getName());
}
} while ((loop = loop.getSuperclass()) != Object.class);
@@ -816,6 +886,7 @@ public final class Rest {
Label lif = new Label();
mv.visitJumpInsn(IFNULL, lif); //if(bean != null) {
for (Map.Entry<String, Object[]> en : attrParaNames.entrySet()) {
RestUploadFile ru = (RestUploadFile) en.getValue()[3];
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, en.getKey(), attrDesc);
mv.visitVarInsn(ALOAD, maxLocals);
@@ -844,6 +915,28 @@ public final class Rest {
mv.visitFieldInsn(GETFIELD, newDynName, typefieldname, "Ljava/lang/reflect/Type;");
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBodyJson", "(Ljava/lang/reflect/Type;)Ljava/lang/Object;", false);
mv.visitTypeInsn(CHECKCAST, Type.getInternalName((Class) en.getValue()[1]));
} else if (en.getKey().contains("_uploadbytes_")) {
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()Lorg/redkale/net/http/MultiContext;", false);
mv.visitLdcInsn(ru.maxLength());
mv.visitLdcInsn(ru.fileNameReg());
mv.visitLdcInsn(ru.contentTypeReg());
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/net/http/MultiContext", "partsFirstBytes", "(JLjava/lang/String;Ljava/lang/String;)[B", false);
} else if (en.getKey().contains("_uploadfile_")) {
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/net/http/HttpRequest", "getMultiContext", "()Lorg/redkale/net/http/MultiContext;", false);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "_redkale_home", "Ljava/io/File;");
mv.visitLdcInsn(ru.maxLength());
mv.visitLdcInsn(ru.fileNameReg());
mv.visitLdcInsn(ru.contentTypeReg());
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/net/http/MultiContext", "partsFirstFile", "(Ljava/io/File;JLjava/lang/String;Ljava/lang/String;)Ljava/io/File;", false);
} else if (en.getKey().contains("_uploadfiles_")) {
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/net/http/HttpRequest", "getMultiContext", "()Lorg/redkale/net/http/MultiContext;", false);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "_redkale_home", "Ljava/io/File;");
mv.visitLdcInsn(ru.maxLength());
mv.visitLdcInsn(ru.fileNameReg());
mv.visitLdcInsn(ru.contentTypeReg());
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/net/http/MultiContext", "partsFiles", "(Ljava/io/File;JLjava/lang/String;Ljava/lang/String;)[Ljava/io/File;", false);
}
mv.visitMethodInsn(INVOKEINTERFACE, attrInternalName, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V", true);
}

View File

@@ -0,0 +1,48 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
*
* 依附在RestService类的方法的参数上, 用于接收上传文件 <br>
* 只能标记在byte[]/File/File[] 类型的参数上 <br>
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Inherited
@Documented
@Target({PARAMETER, FIELD})
@Retention(RUNTIME)
public @interface RestUploadFile {
/**
* 可接收的文件大小最大值, 小于1表示无大小限制
*
* @return int
*/
long maxLength() default 0;
/**
* 可接收的文件名正则表达式, 为空表示接收任何文件 <br>
*
* @return String
*/
String fileNameReg() default "";
/**
* 可接收的ContentType正则表达式, 为空表示接收任何文件类型 <br>
*
* @return String
*/
String contentTypeReg() default "";
}

View File

@@ -29,6 +29,9 @@ public class HelloEntity {
@RestBody
private byte[] bodys;
@RestUploadFile
private byte[] uploads;
@RestBody
private Map<String, String> bodymap;
@@ -123,6 +126,14 @@ public class HelloEntity {
this.bodymap = bodymap;
}
public byte[] getUploads() {
return uploads;
}
public void setUploads(byte[] uploads) {
this.uploads = uploads;
}
@Override
public String toString() {
return JsonFactory.root().getConvert().convertTo(this);