Серия Netty: создание файлового сервера загрузки HTTP

Java HTTP Netty

Это 31-й день моего участия в августовском испытании обновлений. Узнайте подробности события:Испытание августовского обновления

Введение

В предыдущей статье мы говорили о том, как загружать файлы с HTTP-сервера, и о проблемах, на которые следует обратить внимание при создании файлового сервера загрузки с использованием метода GET. В этой статье мы обсудим широко используемый метод POST для отправки данных на сервер и способы загрузки файлов на сервер.

Метод GET для загрузки данных

Согласно спецификации HTTP, PUT обычно загружает данные на сервер. Хотя это не рекомендуется, GET также может использоваться для загрузки данных на сервер.

Давайте рассмотрим вопросы, на которые необходимо обратить внимание при построении клиента GET.

Запрос GET на самом деле является URI. За URI следуют параметры запроса. Netty предоставляет QueryStringEncoder для создания содержимого параметра:

// HTTP请求
        QueryStringEncoder encoder = new QueryStringEncoder(get);
        // 添加请求参数
        encoder.addParam("method", "GET");
        encoder.addParam("name", "flydean");
        encoder.addParam("site", "www.flydean.com");
        URI uriGet = new URI(encoder.toString());

С URI запроса вы можете создать HttpRequest.Конечно, этот HttpRequest также должен иметь соответствующие данные заголовка HTTP:

HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uriGet.toASCIIString());
        HttpHeaders headers = request.headers();
        headers.set(HttpHeaderNames.HOST, host);
        headers.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
        headers.set(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP + "," + HttpHeaderValues.DEFLATE);
        headers.set(HttpHeaderNames.ACCEPT_LANGUAGE, "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
        headers.set(HttpHeaderNames.REFERER, uriSimple.toString());
        headers.set(HttpHeaderNames.USER_AGENT, "Netty Simple Http Client side");
        headers.set(HttpHeaderNames.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");

        headers.set(
                HttpHeaderNames.COOKIE, ClientCookieEncoder.STRICT.encode(
                        new DefaultCookie("name", "flydean"),
                        new DefaultCookie("site", "www.flydean.com"))
        );

Мы знаем, что в HttpRequest есть только две части данных, а именно HttpVersion и HttpHeaders. HttpVersion — это номер версии протокола HTTP, а HttpHeaders — заданное содержимое заголовка.

Для запросов GET, поскольку все содержимое содержится в URI, дополнительный HTTPContent не требуется, и HttpRequest можно отправить непосредственно на сервер.

channel.writeAndFlush(request);

Затем посмотрите, как сервер обрабатывает запрос GET после его получения.

После того, как серверная сторона получит msg объекта HttpObject, ей необходимо преобразовать его в объект HttpRequest, а затем получить соответствующую информацию через protocolVersion(), uri() и headers().

Для параметров в URI netty предоставляет класс QueryStringDecoder для простого анализа параметров в URI:

//解析URL中的参数
            QueryStringDecoder decoderQuery = new QueryStringDecoder(request.uri());
            Map<String, List<String>> uriAttributes = decoderQuery.parameters();
            for (Entry<String, List<String>> attr: uriAttributes.entrySet()) {
                for (String attrVal: attr.getValue()) {
                    responseContent.append("URI: ").append(attr.getKey()).append('=').append(attrVal).append("\r\n");
                }
            }

Метод POST для загрузки данных

Для POST-запросов он имеет на один HTTPContent больше, чем GET-запросы, а это значит, что в дополнение к базовым данным HttpRequest также требуется PostBody.

Если это просто обычный POST, то есть содержимое POST в виде ключ=значение, это относительно просто.Если POST содержит файлы, это будет сложнее, и ENCTYPE="multipart/form-data" необходимо использовать.

Netty предоставляет класс HttpPostRequestEncoder для быстрого кодирования тела запроса. Сначала взгляните на полный конструктор класса HttpPostRequestEncoder:

public HttpPostRequestEncoder(
            HttpDataFactory factory, HttpRequest request, boolean multipart, Charset charset,
            EncoderMode encoderMode)

Откуда запрос - это кодировка HTTPREQUEST, MultipAld указывает, является ли он в формате «Multipart / Form-data data», а метод кодирования Charset является CHARSETUTIL.UTF_8 по умолчанию. Encodermode - это режим кодирования, в настоящее время существует три режима кодирования, а именно RFC1738, RFC3986 и HTML5.

Режим кодирования по умолчанию — RFC1738, именно так кодируется большинство данных отправки формы. Но это не работает с OAUTH, если вы хотите использовать OAUTH, вы можете использовать RFC3986. HTML5 отключает гибридный режим multipart/form-data.

Наконец, давайте поговорим о HttpDataFactory. factory в основном используется для создания InterfaceHttpData. Имеет параметр minSize, если размер созданного HttpData больше minSize, то он будет храниться на диске, иначе будет создаваться прямо в памяти.

InterfaceHttpData имеет три типа HttpData, а именно Attribute, FileUpload и InternalAttribute.

Атрибут — это значение атрибута, переданное в запросе POST. FileUpload — это файл, передаваемый в POST-запросе, а InternalAttribute используется внутри кодировщика, поэтому я не буду здесь подробно его обсуждать.

Поэтому по входящему размеру параметра minSize Attribute и FileUpload можно разделить на следующие типы:

MemoryAttribute, DiskAttribute or MixedAttribute MemoryFileUpload, DiskFileUpload or MixedFileUpload

В этом разделе рассмотрим метод обработки отказа от загрузки файлов в POST-запросе.Сначала создадим HTTP-запрос и кодировщик PostBody:

// 构建HTTP request
        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriSimple.toASCIIString());

 HttpPostRequestEncoder bodyRequestEncoder =
                new HttpPostRequestEncoder(factory, request, false);  

Добавьте заголовки к запросу:

// 添加headers
        for (Entry<String, String> entry : headers) {
            request.headers().set(entry.getKey(), entry.getValue());
        }

Затем добавьте атрибут формы в bodyRequestEncoder:

// 添加form属性
        bodyRequestEncoder.addBodyAttribute("method", "POST");
        bodyRequestEncoder.addBodyAttribute("name", "flydean");
        bodyRequestEncoder.addBodyAttribute("site", "www.flydean.com");
        bodyRequestEncoder.addBodyFileUpload("myfile", file, "application/x-zip-compressed", false);

Обратите внимание, что выше мы добавили свойства метода, имени и сайта в bodyRequestEncoder. Затем добавил FileUpload. Но поскольку наша кодировка не является "multipart/form-data", мы передаем только имя файла, а не весь файл.

Наконец, нам нужно вызвать метод finalizeRequest для bodyRequestEncoder, чтобы вернуть окончательный запрос для отправки. В процессе finalizeRequest также устанавливается, будет ли кодирование передачи фрагментировано в соответствии с размером передаваемых данных.

Если передаваемый контент относительно большой, его нужно передавать сегментами, в этом случае необходимо установить transfer-encoding = chunked, иначе он не будет установлен.

Наконец, отправьте запрос:

// 发送请求
        channel.write(request);

На стороне сервера нам также нужно создать HttpDataFactory, а затем использовать эту фабрику для создания HttpPostRequestDecoder для декодирования данных из кодировщика:

HttpDataFactory factory =
            new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE);
//POST请求
decoder = new HttpPostRequestDecoder(factory, request);

Поскольку сторона сервера сообщений в зависимости от длины полученного сообщения может быть отправлена, может быть HttpContent, это может быть LastHttpContent. Если HttpContent, мы проанализируем результаты в StringBuilder в кэше, и, таким образом, получим LastHttpContent, а затем отправим их вместе.

После получения HttpContent мы вызываем метод decoder.offer для декодирования HttpContent:

decoder.offer(chunk);

Внутри декодера есть два контейнера для хранения данных HttpData, а именно:

List<InterfaceHttpData> bodyListHttpData
和
Map<String, List<InterfaceHttpData>> bodyMapHttpData

decoder.offer должен проанализировать фрагмент, а затем заполнить проанализированные данные в bodyListHttpData и bodyMapHttpData.

После синтаксического анализа проанализированные данные могут быть прочитаны.

BodyListHttpData можно пройти через методы hasNext и next декодера для получения соответствующего InterfaceHttpData.

Тип данных InterfaceHttpData можно получить с помощью data.getHttpDataType().Как упоминалось выше, существует два типа Attribute и FileUpload.

Метод POST для загрузки файлов

Если вы хотите отправить файл POST, клиент может передать multipart=true при создании HttpPostRequestEncoder:

 HttpPostRequestEncoder bodyRequestEncoder =
                new HttpPostRequestEncoder(factory, request, true);

Затем вызовите методы setBodyHttpDatas и finalizeRequest соответственно, чтобы сгенерировать HttpRequest и записать в канал:

// 添加body http data
        bodyRequestEncoder.setBodyHttpDatas(bodylist);
        // finalize request,判断是否需要chunk
        request = bodyRequestEncoder.finalizeRequest();
        // 发送请求头
        channel.write(request);

Следует отметить, что если transfer-encoding = chunked, то этот HttpRequest — это только информация заголовка запроса, нам также нужно вручную прописать HttpContent в канал:

        // 判断bodyRequestEncoder是否是Chunked,发送请求内容
        if (bodyRequestEncoder.isChunked()) {
            channel.write(bodyRequestEncoder);
        }

На стороне сервера, судя по getHttpDataType интерфейсаHttpData, если это тип FileUpload, это означает, что загруженный файл получен, и содержимое файла может быть прочитано следующим методом:

FileUpload fileUpload = (FileUpload) data;
responseContent.append(fileUpload.getString(fileUpload.getCharset()));

Таким образом, мы можем получить файлы, отправленные клиентом на стороне сервера.

Суммировать

Есть много проблем, которые необходимо учитывать при загрузке файлов по HTTP. Если вы не понимаете, вы можете обратиться к моему примеру. Или напишите мне для обсуждения.

Примеры этой статьи могут относиться к:learn-netty4

Эта статья была включена вWoohoo.Floyd Press.com/21-Netty-Contract…

Самая популярная интерпретация, самая глубокая галантерея, самые краткие уроки и множество трюков, о которых вы не знаете, ждут вас!

Добро пожаловать, чтобы обратить внимание на мой официальный аккаунт: «Программируйте эти вещи», разбирайтесь в технологиях, лучше поймите себя!