您现在的位置是:主页 > news > 单位网站的作用/交友网站有哪些

单位网站的作用/交友网站有哪些

admin2025/4/21 13:35:49news

简介单位网站的作用,交友网站有哪些,个人网站可以做百度搜索妈妈,北京做建筑信息的网站1、文件上传 文件上传核心核心要点: 文件通过前端表单或者ajax提交,文件上传应该使用enctype"multipart/form-data"标签。 前端文件上传是面向多用户的,多用户之间可能存在上传同一个名称、类型的文件;为了避免文件冲…

单位网站的作用,交友网站有哪些,个人网站可以做百度搜索妈妈,北京做建筑信息的网站1、文件上传 文件上传核心核心要点: 文件通过前端表单或者ajax提交,文件上传应该使用enctype"multipart/form-data"标签。 前端文件上传是面向多用户的,多用户之间可能存在上传同一个名称、类型的文件;为了避免文件冲…

1、文件上传

文件上传核心核心要点:

  • 文件通过前端表单或者ajax提交,文件上传应该使用enctype="multipart/form-data"标签。

  • 前端文件上传是面向多用户的,多用户之间可能存在上传同一个名称、类型的文件;为了避免文件冲突导致的覆盖问题这些应该在后台进行解决!

  • 对于文件名称采用UUID、雪花算法、MD5等一些哈希手段确保不会重复;

  • 对于用户上传的文件不能让用户轻易的获取到,应该将上传的文件放在一个相对隐秘的或者禁止的路径中。

  • 针对不同场景应该限制用户上传文件的类型、大小;

  • 后台在处理文件上传的时候应该不应该占用主线程,应该使用异步的形式处理文件上传;主线程继续向下执行代码,异步的优势在于页面不会白屏转圈太久增强用户体验!


2、文件上传简单实现

2.1、编写前端页面
  1. 文件上传请求类型必须是post请求
  2. 同时必须是enctype=“multipart/form-data”
  3. 可以通过accept设置上传文件的类型
  4. 多文件可以使用ctrl多选,标签中携带上multiple
<!DOCTYPE html>
<html lang="en" xml>
<head><meta charset="UTF-8"><title>文件上传</title>
</head>
<body><form method="post" action="/upload" enctype="multipart/form-data">单文件: <input type="file" name="headimg"><br/><hr/>多文件: <input type="file" name="photos" multiple><br/><input type="submit" value="上传"></form>
</body>
</html>
2.2、Controller层
  • 依据上传核心应该使用异步的形式,因此Controller线程中不应该直接对文件处理;而应该将文件交由Service层进行异步处理,Controller线程继续向下执行处理未执行完毕的代码!

  • @RequestPart注解用于标注文件上传参数

  • MultipartFile参数是一个封装IO流的简易文件处理接口,StandardMultipartFile实现类。

@Controller
public class FileController {@AutowiredFileUploadService service;@RequestMapping("/upload")@ResponseBodypublic String upload(@RequestPart MultipartFile headimg,@RequestPart MultipartFile[] photos) throws IOException {System.out.println(" Controller线程: =============== "+Thread.currentThread().getName()+" ===========");System.out.println("头像大小: " + headimg.getSize());System.out.println("照片数量: " + photos.length);service.upload(new MultipartFile[]{headimg});service.upload(photos);return "File Upload Success!";}
}
2.3、Service层异步
  • 针对用户上传的文件判断文件是否存在、是否为空之类的东西。

  • 由于需要对文件进行哈希避免冲突,因此需要将文件的类型从名称中截取出来、然后另外使用哈希给文件生成一个随机名称并且拼接文件类型!

@Service
@EnableAsync
public class FileUploadService {@Asyncpublic void upload(MultipartFile[] file) throws IOException {System.out.println(" =========================== "+Thread.currentThread().getName()+" ===========");int length = file.length;if(length > 0){for(int i = 0;i < length;i++){// 获取文件的类型String type = file[i].getOriginalFilename().substring(file[i].getOriginalFilename().lastIndexOf("."));System.out.println(type);// UUID、雪花算法、MD5等一些哈希算法对文件名进行特殊处理,避免文件重名String name = UUID.randomUUID().toString();file[i].transferTo(new File("C:\\Users\\Splay\\Desktop\\上传的文件\\" + name + type));}}System.out.println("上传完毕!");}
}
2.4、参数配置
  • springboot可以支持自定义的参数配置,用于限制上传文件的大小。
spring:    servlet:multipart:enabled: truemax-file-size: 10MB				# 单个文件大小max-request-size: 100MB			# 多文件总大小

请添加图片描述


3、文件上传原理

  • 首先文件上传是通过请求发送出去的,那么肯定在中央调度DispatcherServlet中。

  • 任何数据在网络传输的时候都是01比特串,因此只需要将文件上传与普通参数一同看待即可!

```java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {// 1. 保存一个额外请求processedRequest HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;// 这里检查是否异步请求    暂时忽略WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {// 2. 检查是不是文件上传的请求processedRequest = checkMultipart(request);// 3. 判断检查前后请求是否一致multipartRequestParsed = (processedRequest != request);// 4. 拿到HandlerExecution执行链mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// 查找适配器HandlerAdapterHandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// 请求方式解析String method = request.getMethod();boolean isGet = HttpMethod.GET.matches(method);if (isGet || HttpMethod.HEAD.matches(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}// 前置拦截器调用if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 5. 所有参数解析并且执行mv = ha.handle(processedRequest, response, mappedHandler.getHandler());applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);......// 善后处理}}
}
3.1、整体调度
  1. 先将请求当做一个普通请求processedRequest,然后checkMultipart(request)检查本次请求是否是文件上传。

  2. 检查的方式很简单通过StandardServletMultipartResolver类判断form表单中的contentType是否为enctype=“multipart/form-data”。

    public class StandardServletMultipartResolver implements MultipartResolver {@Overridepublic boolean isMultipart(HttpServletRequest request) {return StringUtils.startsWithIgnoreCase(request.getContentType(),(this.strictServletCompliance ? MediaType.MULTIPART_FORM_DATA_VALUE : "multipart/"));}
    }
    @Override
    public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {// 返回一个文件上传请求的对象return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
    }
    
  3. 如果是文件上传那么会将本次请求调用resolveMultipart进行解析一下并且封装成一个新的请求。此时processRequest 一定不等于 request。

  4. 之后就是拿到HandlerExecution执行链、查找HandlerAdapter适配器、请求方式method解析、调用preHandler前置拦截器做拦截。

3.2、设置与校验
  1. 即上面执行完毕后,来到ha.handle()方法;所有上面在执行controller时没做的东西都会在这里执行(请求方式验证、参数解析、反射调用controller…)

  2. 并且在这里会设置一堆的东西,例如:参数解析器(不同注解、类型的参数由不同的解析器)、数据绑定器(DataBinder),之后数据解析与绑定就是交由DataBinder做。

  3. 再一堆杂七杂八的设置之后来到invokeForRequest方法,拿到参数之后调用doInvoke()反射执行controller。

public class InvocableHandlerMethod extends HandlerMethod {@Nullablepublic Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 参数解析Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);if (logger.isTraceEnabled()) {logger.trace("Arguments: " + Arrays.toString(args));}return doInvoke(args);				//执行Controller}
}
3.3、参数解析大致流程
  • 首先要避开一个弯,参数是在调用controller之前解析完毕的

  • 不同参数使用不同的参数解析器,这里采用了策略模式,supportsParameter方法中是一个增强for循环;匹配合适的直接丢入map中,在第4步的解析中直接从map中获取!

  • 整个方法核心就是不同参数是如何适配到解析器的、参数又是如何解析的。

public class InvocableHandlerMethod extends HandlerMethod {protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 1. 拿到前端上传的所有参数名称MethodParameter[] parameters = getMethodParameters();if (ObjectUtils.isEmpty(parameters)) {return EMPTY_ARGS;}// 2. 参数分配空间Object[] args = new Object[parameters.length];for (int i = 0; i < parameters.length; i++) {MethodParameter parameter = parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);args[i] = findProvidedArgument(parameter, providedArgs);if (args[i] != null) {continue;}// 3. 参数解析器的适配,不同参数会使用不同解析器if (!this.resolvers.supportsParameter(parameter)) {throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));}try {// 4. 参数解析args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);}....}return args;}
}
3.4、参数解析器的适配

这里只是适配每一个参数的解析器、并不会解析参数;因此缓存池是非常有必要的,下次解析参数就可以直接从缓存池中拿!

@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {// 缓存池便于HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);if (result == null) {for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {if (resolver.supportsParameter(parameter)) {result = resolver;this.argumentResolverCache.put(parameter, result);break;}}}return result;
}
  • 文件上传参数的解析器的适配是通过RequestPartMethodArgumentResolver类判断的。

  • 这里直接判断参数上的注解类型是否为@RequestPart,而参数的信息在之前执行过程中就已经全部拿到了。

  • 判断为true之后这个RequestPartMethodArgumentResolver解析器就会被扔到上面的缓存池中便于下次直接获取

public boolean supportsParameter(MethodParameter parameter) {// 直接判断参数上的注解类型是否为@RequestPartif (parameter.hasParameterAnnotation(RequestPart.class)) {return true;}else {if (parameter.hasParameterAnnotation(RequestParam.class)) {return false;}return MultipartResolutionDelegate.isMultipartArgument(parameter.nestedIfOptional());}
}

在这里插入图片描述

3.5、参数解析
  • 由于前面铺垫太多东西,参数解析就变得非常简单了。缓存拿到对应的解析器、然后解析
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) {// map缓存池拿解析器HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);if (resolver == null) {throw new IllegalArgumentException("Unsupported parameter type [" +parameter.getParameterType().getName() + "]. supportsParameter should be called first.");}// 解析文件return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
  • 这里整体的流程就是先拿到参数注解判断注解中的属性情况,是否required、是否为空…

  • 然后resolveMultipartArgument()方法判断是单文件还是多文件上传

  • 找到对应的HttpMessageConvert转换器进行对应参数数据到目标参数类型的解析

  • 最后将转换器交由DataBinder进行解析与数据绑定。

@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest request, @Nullable WebDataBinderFactory binderFactory) {HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);Assert.state(servletRequest != null, "No HttpServletRequest");// 拿到参数注解RequestPart requestPart = parameter.getParameterAnnotation(RequestPart.class);// 注解是否必须,且是否为空boolean isRequired = ((requestPart == null || requestPart.required()) &&!parameter.isOptional());// 参数名String name = getPartName(parameter, requestPart);parameter = parameter.nestedIfOptional();Object arg = null;// 这里判断是否文件上传、并且是单文件还是多文件上传Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {arg = mpArg;...HttpInputMessage inputMessage = new RequestPartServletServerHttpRequest(servletRequest, name);// 拿到convert转换器arg = readWithMessageConverters(inputMessage, parameter, parameter.getNestedGenericParameterType());if (binderFactory != null) {...// dataBinder参数解析,这里结束文件就成型了!WebDataBinder binder = binderFactory.createBinder(request, arg, name);....return adaptArgumentIfNecessary(arg, parameter);
}