Анализ технологии загрузки файлов Spring Boot на основе алгоритма кодирования/декодирования Base64

Spring Boot

Одна из наиболее распространенных функций веб-приложений при загрузке файлов. Традиционная загрузка файлов требует настройки специальной формы для загрузки файлов. Если взять загрузку изображений в качестве примера, обычный метод заключается в том, чтобы сначала загрузить изображения, затем вернуть адрес изображения и, наконец, использовать картина. Это, несомненно, приведет к серьезной проблеме: если веб-запрос прервется или запрос будет закрыт по другим причинам при последующем использовании образа, то неиспользованные грязные данные останутся на сервере, и их можно будет очистить другим способом. Я называю этот режим проектирования режимом «грубой экономии».Независимо от того, потребляет ли его рынок (бизнес), он будет производиться (загружаться) первым, что в конечном итоге приведет к крайней трате ресурсов. На этот раз я расскажу о другом режиме проектирования, который я называю режимом «экономики, ориентированной на сохранение». алгоритм используется таким образом, двоичный текст синхронно передается в бизнес-метод, и, наконец, файл декодируется и сохраняется для достижения эффекта экономии ресурсов.

основная терминология

1. Кодировка Base64

Кодирование Base64 — это процесс преобразования двоичного кода в символ, который можно использовать для передачи более длинной идентификационной информации в среде HTTP. Например, в системе Java Persistence Hibernate Base64 используется для кодирования длинного уникального идентификатора (обычно 128-битного UUID) в строку, которая используется в качестве параметра в формах HTTP и URL-адресах HTTP GET. В других сценариях приложений часто необходимо кодировать двоичные данные в форму, подходящую для размещения в URL-адресах (включая скрытые поля формы). В настоящее время кодировка Base64 не читается, и ее необходимо декодировать, прежде чем ее можно будет прочитать.

2. Загрузка файла

Загрузка файлов — это передача информации с персонального компьютера (локального компьютера) на центральный сервер (удаленный компьютер) системы для доступа других пользователей в сети. Загрузка файлов делится на загрузку через Интернет и загрузку по FTP. Первая может управляться напрямую, щелкнув ссылку на веб-странице, а для работы последней требуется специальный инструмент FTP.

Анализ случая

Принимая во внимание необходимость добавления статей в качестве примера, статья должна иметь идентификатор, заголовок, обложку, введение, основную часть и другую информацию. Для настройки обложки статьи обычной практикой является асинхронная загрузка изображения на сервер на странице добавления статьи, а затем возврат адреса хранения изображения (URL или URI), привязанного к скрытому полю и одному для предварительного просмотра. , на узле IMG. В настоящее время основная информация статьи не отправлена ​​на сервер, но изображения, относящиеся к статье, поступили на сервер раньше статьи. Хотя это кажется процессом «отчуждения», он всегда кажется «отвратительным». Изначально после смыва унитаза (отправки запроса) и смыва унитаза (отправки транзакции) теперь нужно дополнительно вытирать блевотину на землю (очищать мусорные файлы).

Основываясь на вышеизложенном опыте применения, предлагается использовать кодирование/декодирование Base64 для синхронной загрузки файлов, чтобы изображения статьи поступали на сервер вместе с основной информацией статьи.Если служба неожиданно завершается во время запроса процесс, сервер не будет производить никаких грязных данных. Обсуждено так много потребностей и отправных точек.Далее давайте перейдем к теме этого обмена и посмотрим, как реализовать функцию синхронной загрузки файлов.

Реализация функции

1. Декодер

Нам нужно определить декодер для декодирования двоичных данных изображения, переданных внешним интерфейсом.Как кодировать файл изображения с использованием алгоритма Base64 внешним интерфейсом, будет представлено отдельно в следующем содержании. В настоящее время основной функцией декодера является получение информации о заголовке (метод кодирования) и информации о типе файла в двоичном тексте, закодированном в формате Base64. Затем поле данных декодируется. После завершения декодирования байт-код преобразуется в знакомый объект типа MultipartFile. Код реализации декодера выглядит следующим образом:

package com.ramostear.jfast.common.utils;

import org.springframework.web.multipart.MultipartFile;

import java.io.*;

/**
 * @author ramostear|谭朝红
 * @create-time 2019/3/19 0019-23:54
 * @modify by :
 * @since:
 */
public class Base64Decoder implements MultipartFile{

    private final byte[] IMAGE;

    private final String HEADER;

    private Base64Decoder(byte[]image,String header){
        this.IMAGE = image;
        this.HEADER = header;
    }

    public static MultipartFile multipartFile(byte[]image,String header){
        return new Base64Decoder(image,header);
    }

    @Override
    public String getName() {
        return System.currentTimeMillis()+Math.random()+"."+HEADER.split("/")[1];
    }

    @Override
    public String getOriginalFilename() {
        return System.currentTimeMillis()+(int)Math.random()*10000+"."+HEADER.split("/")[1];
    }

    @Override
    public String getContentType() {
        return HEADER.split(":")[1];
    }

    @Override
    public boolean isEmpty() {
        return IMAGE == null || IMAGE.length == 0;
    }

    @Override
    public long getSize() {
        return IMAGE.length;
    }

    @Override
    public byte[] getBytes() throws IOException {
        return IMAGE;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        return new ByteArrayInputStream(IMAGE);
    }

    @Override
    public void transferTo(File file) throws IOException, IllegalStateException {
        new FileOutputStream(file).write(IMAGE);
    }
}

2. Преобразователь

Теперь вам нужно определить преобразователь для преобразования информации о символах изображения, переданной из внешнего интерфейса, в массив байтов в кодировке Base64, а затем вызвать декодер, чтобы получить окончательный объект типа MultipartFile. Реализация преобразователя относительно проста, код преобразователя выглядит следующим образом:

package com.ramostear.jfast.common.utils;

import org.springframework.web.multipart.MultipartFile;

import java.util.Base64;

/**
 * @author ramostear|谭朝红
 * @create-time 2019/3/20 0020-0:00
 * @modify by :
 * @since:
 */
public class Base64Converter {

    public static MultipartFile converter(String source){
        String [] charArray = source.split(",");
        Base64.Decoder decoder = Base64.getDecoder();
        byte[] bytes = new byte[0];
        bytes = decoder.decode(charArray[1]);
        for (int i=0;i<bytes.length;i++){
            if(bytes[i]<0){
                bytes[i]+=256;
            }
        }
        return Base64Decoder.multipartFile(bytes,charArray[0]);
    }
}

Сосредоточьтесь на методе конвертера:

Во-первых, давайте посмотрим на формат двоичных символов изображения, закодированного на основе алгоритма Base64:

data:image/png;base64,iVBOR....Px1yGQ9EOFXNAAAAAE1FTkSuQmcc

Поэтому сначала разделите строку на ",", получите информацию заголовка данных ***data:image/png;base64***, а затем перекодируйте основную часть данных через Base64 для получения байтового массива, и наконец Вызовите метод декодирования декодера, чтобы получить объект MultipartFile.

3. Кодировка интерфейса Base64

Основная логика серверной части завершена, теперь мы познакомимся с тем, как интерфейс кодирует изображение с помощью алгоритма Base64.

  • Прежде всего, вам нужно иметь форму для добавления статей и установить поле изображения в скрытое состояние, предоставив узел dom для предварительного просмотра изображения и поле ввода для просмотра локальных изображений.Основной код формы выглядит следующим образом. :

    ...
     <form action="/articles" method="POST">
         ...
         <div class="file-preview">
              <div class="file-upload-zone">
                    <div class="file-upload-zone-title">Upload & preview img here …</div>
              </div>
         </div>
         <div class="clearfix"></div>
         <input type="hidden" name="cover" id="cover"/>
         <div class="input-group-btn">
             <button class="btn btn-blue" type="button" id="upload-btn">
                 <i class="fa fa-folder-open"></i>
                 <input id="upload-cover" name="upload-cover" multiple="multiple"
                        onchange="fileChange(this)" type="file"
                        accept="image/*"/>
             </button>
         </div>
         ...
    </form>
    ...
    
  • Затем определите метод fileChange для обработки кодировки файла, код выглядит следующим образом:

    function fileChange(obj){
            try{
                var file = obj.files[0];
                var reader = new FileReader();
                var fileName="";
                if(typeof(fileName) != "undefined"){
                    fileName = $(obj).val().split("\\").pop();
                }
                reader.onload = function(){
                    var img = new Image();
                    img.src = reader.result;
                    img.onload = function(){
                        var w = img.width,h = img.height;
                        var canvas = document.createElement("canvas");
                        var ctx = canvas.getContext("2d");
                        $(canvas).attr({
                            width:w,
                            height:h
                        });
                        ctx.drawImage(img,0,0,w,h);
                        var base64 = canvas.toDataURL("image/png",0.5);
                        var result = {
                            url:window.URL.createObjectURL(file),
                            base64:base64,
                            clearBase64:base64.substr(base64.indexOf(',')+1),
                          suffix:base64.substring(base64.indexOf(',')+1,base64.indexOf(';'))
                        };
                        $(".file-upload-zone-title").hide();
                        $(".file-upload-zone").empty();
                        $("#cover").val(result.base64);
                        $("<img src=\""+result.base64+"\" class=\"img img-responsive center-block\">").appendTo(".file-upload-zone");
                        $(".file-upload-zone").trigger("create");
                        $(".file-name").val(fileName);
                    }
                }
                reader.readAsDataURL(obj.files[0]);
            }catch(e){
                layer.msg("error");
            }
        };
    

Что касается основной логики этого кода, то на самом деле он противоположен процессу декодирования в серверной части, поэтому я не буду здесь вдаваться в подробности.

На данный момент основная функция синхронной загрузки файлов с помощью кодировки Base64 выполнена.В следующем контенте Spring Boot 2.0 используется для быстрой демонстрации контента, которым на этот раз поделились.

Добавить сервисный компонент статьи#загрузка файла

1. Добавьте служебную составляющую статьи

Следуя фону требования с самого начала, информация об изображении принадлежит значению атрибута объекта статьи, поэтому логика для обработки загрузки файла размещена в службе.В этом тестовом коде окончательное хранилище файлов использует службу CDN Qiniuyun. . Код для части CDN не расширяется и может быть загружен локально. Объектами обеих операций являются MultipartFile, и способ хранения не является предметом этого совместного использования. Основной код сервисного компонента статьи выглядит следующим образом:

package com.ramostear.jfast.domain.service.impl;

import com.ramostear.jfast.common.ext.Translate;
import com.ramostear.jfast.domain.repo.ArticleRepo;
import com.ramostear.jfast.domain.service.ArticleService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
 * @author ramostear|谭朝红
 * @create-time 2019/3/19 0019-23:37
 * @modify by :
 * @since:
 */
@Service(value = "articleService")
@Transactional(readOnly = true)
public class ArticleServiceImpl implements ArticleService {
    @Autowired
    private ArticleRepo articleRepo;
    
    @Override
    @Transactional
    public void save(ArticleVo vo) {
        Article article = Translate.toArticle(vo);
        articleRepo.save(article);
    }
    

В сервисном компоненте ArticleService задействован класс Translate, его функция заключается в отображении ValueObject, передаваемого из фронтенда, в класс POJO, а также инкапсуляция логики хранения файлов, основной код выглядит следующим образом:

package com.ramostear.jfast.common.ext;

import com.ramostear.jfast.common.factory.CdnFactory;
import com.ramostear.jfast.common.factory.cdn.CdnRepository;
import com.ramostear.jfast.common.utils.Base64Converter;
import com.ramostear.jfast.domain.model.Article;
import com.ramostear.jfast.domain.vo.ArticleVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.web.multipart.MultipartFile;

import java.util.Date;

/**
 * @author ramostear|谭朝红
 * @create-time 2019/3/18 0018-3:39
 * @modify by :
 * @since:
 */
public class Translate {

    private static CdnRepository cdnRepo = CdnFactory.builder(CdnFactory.CdnType.Qiniu);
    
    public static Article toArticle(ArticleVo vo){
        Article article = new Article();
        BeanUtils.copyProperties(vo,article);
        if(StringUtils.isNotBlank(vo.getCover())){
            MultipartFile file = Base64Converter.converter(vo.getCover());
            article.setCover(cdnRepo.save(file));
        }
        return article;
    }
}

Поскольку здесь используется служба CDN Qiniu Cloud, экземпляр хранилища CND получается через класс фабрики CND, который используется для записи файлов в хранилище и возврата адреса доступа к файлу. В дополнение к вышеуказанным методам вы также можете вызвать метод file.transferTo() для записи файла на локальный (сервер приложений) диск.

Детали реализации фабричного класса CND здесь не расширены из-за недостатка места. Если вам нужно узнать больше о том, как использовать CDN SDK, вы можете оставить мне сообщение в конце статьи.

2. Контроллер статей

Наконец, определите контроллер, который будет вызываться при добавлении статей во внешний интерфейс. Основная задача контроллера статей — получить информацию о статье, переданную внешним интерфейсом, а затем вызвать компонент службы статей для завершения работы по добавлению статей. . Основной код выглядит следующим образом:

package com.ramostear.jfast.domain.controller;

@RestController
public class ArticleController{
    @Autowired
    ArticleService articleService;
    
    @Postmapping(value="/articles")
    public ResponseEntity<Object> createArticle(@RequestBody ArticleVo vo){
        try{
            articleService.save(vo);
            return new ResponseEntity<>("已经成功将文字写入数据库",HttpStatus.CREATED);
        }catch(Exception e){
            return new ResponseEntity<>(e.getMessage(),HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
    
}

заключительные замечания

Этот обмен дает только реализацию основных частей, а задействованные знания, такие как CDN, HTML, JS и т. д., не были расширены.Если вы запутались, вы можете оставить сообщение в области комментариев, и мы обсудим это. вместе. Еще раз спасибо за чтение, спасибо~~~

Перепечатайте, пожалуйста, сохраните информацию об авторских правах и не используйте ее в коммерческих целях.

Автор: Тан Чаохун, оригинальное название:Анализ технологии загрузки файлов Spring Boot на основе алгоритма кодирования/декодирования Base64