最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java處理字節(jié)類型數(shù)據(jù)的實(shí)現(xiàn)步驟

 更新時(shí)間:2024年07月15日 08:28:30   作者:趙俠客  
字節(jié)(Byte)是計(jì)算機(jī)信息技術(shù)用于計(jì)量存儲(chǔ)容量的一種基本單位,通常簡(jiǎn)寫(xiě)為B,在ASCII編碼中1Byte可以表示一個(gè)標(biāo)準(zhǔn)的英文字符,包括大寫(xiě)字母、小寫(xiě)字母、數(shù)字、標(biāo)點(diǎn)符號(hào)和控制字符等,本文給大家介紹了Java如何優(yōu)雅的處理字節(jié)類型數(shù)據(jù),需要的朋友可以參考下

前言

字節(jié)(Byte)是計(jì)算機(jī)信息技術(shù)用于計(jì)量存儲(chǔ)容量的一種基本單位,通常簡(jiǎn)寫(xiě)為B,1Byte=8bit,在ASCII編碼中1Byte可以表示一個(gè)標(biāo)準(zhǔn)的英文字符,包括大寫(xiě)字母、小寫(xiě)字母、數(shù)字、標(biāo)點(diǎn)符號(hào)和控制字符等,共128個(gè)不同的字符,如1、2、3、a、b、c都占用一個(gè)Byte,所以1Byte其實(shí)是非常小的單位,比Byte大的單位就是KB,一般一篇博客文字的大小應(yīng)該在幾十KB,比KB大的單位是MB,目前手機(jī)拍攝一張照片的大小大概是幾MB,比MB大的還有GB、TB、PB、EB、ZB、YB,以下是各單位間的轉(zhuǎn)換。

名稱簡(jiǎn)寫(xiě)換算 
比特(Byte)B1B=8bit
千字節(jié)(KiloByte)KB1KB=2^10 B =1024B
兆字節(jié)(Mega Byte)MB1MB=2^10 KB =2^20 B
吉字節(jié)(GigaByte)GB1GB=2^10 MB =2^30 B
太字節(jié)(TeraByte)TB1TB=2^10 GB =2^40 B
拍字節(jié)(PetaByte)PB1PB=2^10 TB =2^50 B
艾字節(jié)(EXAByte)EB1EB=2^10 PB =2^60 B
澤字節(jié)(Zetta Byte)ZB1ZB=2^10 EB =2^70 B
堯字節(jié)(Yotta Byte)YB1YB=2^10 ZB =2^80 B

從字節(jié)有這么多單位可以看出選擇合適的單位可以讓人很直觀有個(gè)大小概念,比如你可以說(shuō)我買(mǎi)了最新款的IPhone15 128GB版本,別人一看就知道是最低配版本了,可能覺(jué)得你是買(mǎi)了丐版的來(lái)裝逼一下,但是你說(shuō)我買(mǎi)最新款的IPhone15Pro 134217728KB版本,別人第一感肯定不知道你買(mǎi)的是一個(gè)丐版,但是會(huì)覺(jué)得你是個(gè)SB。為了精度我一般在數(shù)據(jù)庫(kù)中會(huì)存儲(chǔ)Byte類型,另外也方便我們?cè)诖a中作計(jì)算和比較,返回給用戶時(shí)則會(huì)轉(zhuǎn)成對(duì)用戶友好的單位,例如我們記錄用戶空間使用量在數(shù)據(jù)庫(kù)中會(huì)存儲(chǔ)最小單位Byte

用戶ID空間用量
183142212058073480
167652085350264853
151381439009188728
91521319605924042
240801259223266116
33251139222905087
94011128752330535
38381125023100502

返回給用戶顯示時(shí)會(huì)轉(zhuǎn)成對(duì)用戶友好的單位

由于字節(jié)的單位比較多,所以代碼中會(huì)經(jīng)常出現(xiàn)手動(dòng)單位轉(zhuǎn)換,這樣代碼就不太優(yōu)雅,本文介紹一種優(yōu)雅處理這些字節(jié)轉(zhuǎn)換的方法,接下來(lái)我們以用戶空間使用量為為例,說(shuō)明如何優(yōu)雅的處理這種數(shù)據(jù)格式轉(zhuǎn)換。

應(yīng)用場(chǎng)景

比如我們現(xiàn)在有一個(gè)類似百度云盤(pán)的系統(tǒng),需要記錄用戶云盤(pán)空間使用量,并且后臺(tái)可以設(shè)置用戶云盤(pán)的最大容量。那么我們至少有兩個(gè)接口,一個(gè)是返回用戶當(dāng)前云盤(pán)空間使用量,另一個(gè)是設(shè)置云盤(pán)最大容量

  • 獲取用戶當(dāng)前空間使用量接口
GET http://localhost:80/userSize/1

返回結(jié)果
{
  "id": 1,
  "size": "1.5M"
}

需要解決的問(wèn)題:將數(shù)據(jù)庫(kù)存的1572864格式化成1.5M

  • 設(shè)置用戶最大容量接口
POST http://localhost:80/userSize  
Content-Type: application/json  
  
{  
    "id":1,  
    "maxSize":"10.5G"  
}

需要解決的問(wèn)題:將前端傳的10.5G轉(zhuǎn)成11274289152存入數(shù)據(jù)庫(kù)

解決思路

目前大部分開(kāi)發(fā)框架都使用SpringBootSpringBootJAVA對(duì)象序列化成JSON和將JSON反序列化成JAVA對(duì)象默認(rèn)使用Jackson,那么我們可以自定義Jackson序列化器和反序列器來(lái)達(dá)到此效果。最終效果是:我們?cè)谙胍袷交淖侄沃性黾?code> @ByteFormat(scale = 1)返回時(shí)自動(dòng)將1572864格式化成1.5M,接收時(shí)自動(dòng)將10.5G轉(zhuǎn)成11274289152,這樣是不是很優(yōu)雅?而且項(xiàng)目中所有地方只要增加這個(gè)注解,就自動(dòng)處理這個(gè)格式轉(zhuǎn)換,下次再遇到字節(jié)類型再也不需要去做一大堆的格式轉(zhuǎn)換了。

@Data
public class UserDTO {
    private Long id;
    @ByteFormat(scale = 3)
    private Long size;
    @ByteFormat(scale = 1)
    private Long maxSize;
}

實(shí)現(xiàn)步驟

定義注解ByteFormat

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JsonSerialize(using = ByteFormatSerializer.class)
@JsonDeserialize(using = ByteFormatDeserializer.class)
@JacksonAnnotationsInside
public @interface ByteFormat {
    // 保留精度
    int scale() default 2;
}

Jackson是可以支持自定義序列化器和反序列化器的, 所以基于此我們可以擴(kuò)展實(shí)現(xiàn)一些自定義序列化注解, 就像 @JsonFormat注解對(duì)時(shí)間格式處理一樣。 那我們擴(kuò)展自定義注解原理也很簡(jiǎn)單,主要是利用 @JsonSerialize、@JsonDeserialize、@JacksonAnnotationsInside注解去實(shí)現(xiàn), @JacksonAnnotationsInside是一個(gè)組合注解,主要標(biāo)記在用戶的自定義注解上,那么這個(gè)用戶自定義注解上標(biāo)記的所有其他注解也會(huì)生效。

定義序列化器ByteFormatSerializer

ByteFormatSerializer類的作用是當(dāng)Jackson序列化遇到Number類型時(shí)會(huì)調(diào)用createContextual()方法,在該方法中判斷字段上是否有ByteFormat注解,如果有則告訴Jackson來(lái)調(diào)用ByteFormatSerializerserialize來(lái)序列化,在serialize()方法中完成了數(shù)據(jù)格式的轉(zhuǎn)換。

public class ByteFormatSerializer extends JsonSerializer<Number> implements ContextualSerializer {
    protected ByteFormat byteFormat;
    
    public ByteFormatSerializer(){
    }

    public ByteFormatSerializer(ByteFormat byteFormat){
        this.byteFormat=byteFormat;
    }

    @Override
    public void serialize(Number value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if (value == null){
            return;
        }
        int scale = byteFormat.scale();
        BigDecimal bigValue = new BigDecimal(value.toString());
        String result = ByteConvert.convertValue(bigValue,  scale);
        gen.writeString(result );
    }
    

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
        if (beanProperty != null) {
            if (Objects.equals(beanProperty.getType().getRawClass().getGenericSuperclass(), Number.class) ) {
                ByteFormat t = beanProperty.getAnnotation(ByteFormat.class);
                if (t != null) {
                    return  new ByteFormatSerializer(t);
                }
            }
            return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
        }
        return serializerProvider.findNullValueSerializer(beanProperty);
    }

}

定義反序列化器ByteFormatDeserializer

ByteFormatDeserializer類的作用是Jackson反序列化遇到Number類型時(shí)會(huì)調(diào)用createContextual()方法,在該方法中判斷如果字段上有ByteFormat注解則告訴Jackson來(lái)調(diào)用ByteFormatDeserializerdeserialize方法,在deserialize()方法中完成了數(shù)據(jù)的轉(zhuǎn)換。

public class ByteFormatDeserializer extends JsonDeserializer<Number> implements ContextualDeserializer  {
    protected ByteFormat byteFormat;

    public ByteFormatDeserializer(){
    }

    public ByteFormatDeserializer(ByteFormat byteFormat){
        this.byteFormat=byteFormat;
    }


    @Override
    public Number deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        if (!StringUtils.hasText(p.getText())) {
            return null;
        }
        if(byteFormat!=null){
            String value = p.getText();
            return ByteConvert.convertNumber(value);
        }
        return null;
    }


    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
        if (beanProperty != null) {
            if (Objects.equals(beanProperty.getType().getRawClass().getGenericSuperclass(), Number.class) ) {
                ByteFormat t = beanProperty.getAnnotation(ByteFormat.class);
                if (t != null) {
                    return  new ByteFormatDeserializer(t);
                }
            }
            return serializerProvider.findContextualValueDeserializer(beanProperty.getType(), beanProperty);
        }
        return this;
    }
}


格式轉(zhuǎn)換工具類ByteConvert

public class ByteConvert {
    public static final Long KB=1L<<10;
    public static final Long MB=KB<<10;
    public static final Long GB=MB<<10;
    public static final Long TB=GB<<10;
    
    public static String convertValue(BigDecimal bigValue, int scale) {
        if(bigValue.compareTo(BigDecimal.valueOf(TB))>=0){
            return String.format("%sT",bigValue.divide(BigDecimal.valueOf(TB), scale, RoundingMode.HALF_UP));
        }
        if(bigValue.compareTo(BigDecimal.valueOf(GB))>=0){
            return String.format("%sG",bigValue.divide(BigDecimal.valueOf(GB), scale, RoundingMode.HALF_UP));
        }
        if(bigValue.compareTo(BigDecimal.valueOf(MB))>=0){
            return String.format("%sM",bigValue.divide(BigDecimal.valueOf(MB), scale, RoundingMode.HALF_UP));
        }
        if(bigValue.compareTo(BigDecimal.valueOf(KB))>=0){
            return String.format("%sK",bigValue.divide(BigDecimal.valueOf(KB), scale, RoundingMode.HALF_UP));
        }
        return String.format("%sB",bigValue);
    }

    public static Number convertNumber(String stringValue) {
        if (stringValue.endsWith("T")) {
            Double value = Double.parseDouble(stringValue.replaceAll("T", "")) * TB;
            return value.longValue();
        }
        if (stringValue.endsWith("G")) {
            Double value = Double.parseDouble(stringValue.replaceAll("G", "")) * GB;
            return value.longValue();
        }
        if (stringValue.endsWith("M")) {
            Double value = Double.parseDouble(stringValue.replaceAll("M", "")) * MB;
            return value.longValue();
        }
        if (stringValue.endsWith("K")) {
            Double value = Double.parseDouble(stringValue.replaceAll("K", "")) * KB;
            return value.longValue();
        }
        return Double.valueOf(stringValue).longValue();
    }
}

測(cè)試

編寫(xiě)兩個(gè)測(cè)試接口,一個(gè)接口返回用戶當(dāng)前使用容器量,然后把size大小設(shè)置成1572864,另一個(gè)是設(shè)置用戶最大使用容量,使用UserDTO直接接收。

    @GetMapping("/userSize/{id}")
    public ResponseEntity<UserDTO> userSize(@PathVariable Long id) {
        UserDTO userDTO = new UserDTO();
        userDTO.setId(id);
        userDTO.setSize(1572864L);
        return ResponseEntity.ok(userDTO);
    }

    @PostMapping("/userSize")
    public ResponseEntity<UserDTO> setUserSize(@RequestBody UserDTO userDTO) {
        log.info("user {} maxSize {}", userDTO.getId(), userDTO.getMaxSize());
        return ResponseEntity.ok(userDTO);
    }

可以接口返回用戶使用容量字段size成功格式化成1.5M,當(dāng)然如里返回List<UserDTO>Map中也是能正常格式化的,完全符合預(yù)期

可以看出用戶傳maxSize:10.5G,后端成功使用Long maxSize類型接收到了String類型數(shù)據(jù),并且將String數(shù)值轉(zhuǎn)成了11274289152,完全符合預(yù)期。

總結(jié)

本文使用Jackson自定義了ByteFormat注解,解決了字節(jié)類型數(shù)據(jù)在前端與后端之間的優(yōu)雅轉(zhuǎn)換。當(dāng)然本方法不僅可以解決字節(jié)類型的數(shù)據(jù)格式轉(zhuǎn)換,還可以用于如時(shí)間格式、枚舉格式、金錢(qián)格式的轉(zhuǎn)換,再擴(kuò)展一下也可以用于數(shù)據(jù)脫敏等場(chǎng)景。本解決方法主要有以下優(yōu)點(diǎn):

  • 使用優(yōu)雅:使用者只需要在字段上增加@ByteFormat(scale = 3)即可,代碼很優(yōu)雅
  • 方法通用:該方法不僅可用于http接口參數(shù)的轉(zhuǎn)換,還可用于Jackson數(shù)據(jù)的轉(zhuǎn)換的所有場(chǎng)景
  • 降本增效:該方法完全可以在團(tuán)隊(duì)中推廣,大家都可以使用,不用每個(gè)人寫(xiě)一堆轉(zhuǎn)換
  • 前端友好:前端拿到這樣的接口使用很方便,返回?cái)?shù)據(jù)直接顯示就好,用戶輸入數(shù)據(jù)直接傳后端

當(dāng)然本方法也是有缺點(diǎn)的:

  • 只能用于Jackson:其它JSON序列化工具不支持如使用FastJson、Gson
  • 使用域?qū)嶓w對(duì)象:實(shí)體對(duì)象一旦添加了ByteFormat都會(huì)作格式轉(zhuǎn)換,如果有特殊場(chǎng)景不想做轉(zhuǎn)換則需要使用新實(shí)體對(duì)象

以上就是Java處理字節(jié)類型數(shù)據(jù)的實(shí)現(xiàn)步驟的詳細(xì)內(nèi)容,更多關(guān)于Java處理字節(jié)類型數(shù)據(jù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JDK源碼之線程并發(fā)協(xié)調(diào)神器CountDownLatch和CyclicBarrier詳解

    JDK源碼之線程并發(fā)協(xié)調(diào)神器CountDownLatch和CyclicBarrier詳解

    我一直認(rèn)為程序是對(duì)于現(xiàn)實(shí)世界的邏輯描述,而在現(xiàn)實(shí)世界中很多事情都需要各方協(xié)調(diào)合作才能完成,就好比完成一個(gè)平臺(tái)的交付不可能只靠一個(gè)人,而需要研發(fā)、測(cè)試、產(chǎn)品以及項(xiàng)目經(jīng)理等不同角色人員進(jìn)行通力合作才能完成最終的交付
    2022-02-02
  • Java異常繼承結(jié)構(gòu)解析_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Java異常繼承結(jié)構(gòu)解析_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要介紹了Java異常繼承結(jié)構(gòu)解析的相關(guān)知識(shí),需要的朋友可以參考下
    2017-04-04
  • 最新評(píng)論

    鄂托克旗| 喀什市| 达州市| 铁力市| 四川省| 怀仁县| 沁源县| 阿尔山市| 榆林市| 安塞县| 调兵山市| 隆林| 惠来县| 肥乡县| 普陀区| 北安市| 云浮市| 永德县| 方正县| 东宁县| 禹城市| 武川县| 靖边县| 延津县| 湟源县| 大余县| 巴马| 正蓝旗| 文昌市| 雷波县| 常宁市| 冷水江市| 临武县| 莎车县| 太仓市| 泾阳县| 老河口市| 高安市| 永德县| 新乡县| 丹东市|