AlexanderAsia 2022-12-02 19:23 采纳率: 0%
浏览 44
已结题

关于gateway产生临时文件夹的问题

在使用springcloud gateway(2.2.8.RELEASE)的过程中遇到如下问题:
上传文件时会在临时目录下创建临时文件夹而不会删除,参考解决方案中都是把spring-web的版本升级到5.2.16.RELEASE,然而没有生效,请见代码,目前判断问题出在readFormData方法实现上,但一直未定位到问题所在

         <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
            <version>2.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.2.16.RELEASE</version>
        </dependency>
import cn.hutool.core.collection.CollectionUtil;
import com.xxxx.gateway.entity.GatewayLog;
import com.xxxx.gateway.service.AccessLogService;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ObjectUtils;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.codec.multipart.FormFieldPart;
import org.springframework.http.codec.multipart.Part;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.*;

@Slf4j
@Component
public class AccessLogFilter2 implements GlobalFilter, Ordered {
    @Autowired
    ServerCodecConfigurer codecConfigurer;

    @Override
    public int getOrder() {
        return -100;
    }

    @Override
    @SuppressWarnings("unchecked")
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();

        MediaType mediaType = request.getHeaders().getContentType();
        if (mediaType == null) {
            mediaType = MediaType.APPLICATION_JSON;
        }

        Map<String, String> headers = request.getHeaders().toSingleValueMap();
        Map<String, String> logHeaders = new HashMap<>();
        for (String headerName : AccessLogService.HEADER_NAMES) {
            if (headers.containsKey(headerName)) {
                logHeaders.put(headerName, headers.get(headerName));
            }
        }

        GatewayLog gatewayLog = new GatewayLog();
        exchange.getAttributes().put(AccessLogService.CACHE_GATEWAY_CONTEXT, gatewayLog);
        if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(mediaType) || MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType)) {
            return readFormData(exchange, chain, gatewayLog);
        } else {
            //其他格式
            return chain.filter(exchange);
        }
    }

    private ServerHttpRequest wrapperServerHttpRequest(ServerWebExchange exchange, DataBuffer dataBuffer) {
        DataBufferUtils.retain(dataBuffer);
        final Flux<DataBuffer> cachedFlux = Flux.defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount())));
        return new ServerHttpRequestDecorator(exchange.getRequest()) {
            @Override
            public Flux<DataBuffer> getBody() {
                return cachedFlux;
            }

            @Override
            public MultiValueMap<String, String> getQueryParams() {
                return UriComponentsBuilder.fromUri(exchange.getRequest().getURI()).build().getQueryParams();
            }
        };
    }

    /**
     * 读取form-data数据
     */
    private Mono<Void> readFormData(ServerWebExchange exchange, GatewayFilterChain chain, GatewayLog accessLog) {
        return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> {
            final ServerHttpRequest mutatedRequest = wrapperServerHttpRequest(exchange, dataBuffer);
            final HttpHeaders headers = exchange.getRequest().getHeaders();
            if (headers.getContentLength() == 0) {
                return chain.filter(exchange);
            }
            ResolvableType resolvableType;
            if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(headers.getContentType())) {
                resolvableType = ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, Part.class);
            } else {
                //解析 application/x-www-form-urlencoded
                resolvableType = ResolvableType.forClass(String.class);
            }
            return codecConfigurer.getReaders().stream().filter(reader -> reader.canRead(resolvableType, mutatedRequest.getHeaders().getContentType())).findFirst()
                    .orElseThrow(() -> new IllegalStateException("no suitable HttpMessageReader."))
                    .readMono(resolvableType, mutatedRequest, Collections.emptyMap()).flatMap(resolvedBody -> {
                        if (resolvedBody instanceof MultiValueMap) {
                            MultiValueMap map = (MultiValueMap) resolvedBody;
                            if (CollectionUtil.isNotEmpty(map)) {
                                StringBuilder builder = new StringBuilder();
                                map.forEach((key, val) -> {
                                    final Part valPart = (Part) map.getFirst(key);
                                    if (valPart instanceof FormFieldPart) {
                                        String value = ((FormFieldPart) valPart).value();
                                        if (builder.length() > 0) {
                                            builder.append("&");
                                        }
                                        builder.append(key).append("=").append(value);
                                    }
                                });
                                accessLog.setRequestBody(builder.toString());
                            }
                        } else {
                            accessLog.setRequestBody((String) resolvedBody);
                        }

                        //获取响应体
                        ServerHttpResponseDecorator decoratedResponse = recordResponseLog(exchange, accessLog);
                        return chain.filter(exchange.mutate().request(mutatedRequest).response(decoratedResponse).build());
                    });
        });
    }

    /**
     * 记录响应日志
     */
    private ServerHttpResponseDecorator recordResponseLog(ServerWebExchange exchange, GatewayLog gatewayLog) {
        ServerHttpResponse response = exchange.getResponse();
        DataBufferFactory bufferFactory = response.bufferFactory();
        return new ServerHttpResponseDecorator(response) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {
                    // 获取响应类型,如果是 json 就打印
                    String originalResponseContentType = exchange.getAttribute(ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);
                    HttpStatus statusCode = this.getStatusCode();
                    if (ObjectUtils.equals(statusCode, HttpStatus.OK)
                            && !StringUtil.isNullOrEmpty(originalResponseContentType)
                            && originalResponseContentType.contains("application/json")) {
                        Flux<? extends DataBuffer> fluxBody = Flux.from(body);
                        return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
                            // 合并多个流集合,解决返回体分段传输
                            DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
                            DataBuffer join = dataBufferFactory.join(dataBuffers);
                            byte[] content = new byte[join.readableByteCount()];
                            join.read(content);
                            // 释放掉内存
                            DataBufferUtils.release(join);
                            return bufferFactory.wrap(content);
                        }));
                    } else if (!ObjectUtils.equals(statusCode, HttpStatus.OK)) {
                        // gatewayLog.wrapperResponseContent(statusCode.value(), statusCode.value(), statusCode.getReasonPhrase());
                    }
                }
                // if body is not a flux. never got there.
                return super.writeWith(body);
            }
        };
    }
}

每次上传文件都会在临时目录下创建一个临时文件夹,会导致文件产生过多而报错:Too many links

img

期望不会创建临时文件夹

  • 写回答

2条回答 默认 最新

  • 普通网友 2022-12-02 22:49
    关注

    我建议你贴下更详细的日志提示。

    仅仅只有 Too many links ,怕是不好判断。

    评论

报告相同问题?

问题事件

  • 系统已结题 12月10日
  • 创建了问题 12月2日

悬赏问题

  • ¥20 机器学习能否像多层线性模型一样处理嵌套数据
  • ¥20 西门子S7-Graph,S7-300,梯形图
  • ¥50 用易语言http 访问不了网页
  • ¥50 safari浏览器fetch提交数据后数据丢失问题
  • ¥15 matlab不知道怎么改,求解答!!
  • ¥15 永磁直线电机的电流环pi调不出来
  • ¥15 用stata实现聚类的代码
  • ¥15 请问paddlehub能支持移动端开发吗?在Android studio上该如何部署?
  • ¥20 docker里部署springboot项目,访问不到扬声器
  • ¥15 netty整合springboot之后自动重连失效