欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java處理字節(jié)類(lèi)型數(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é)類(lèi)型數(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)換。

名稱(chēng)簡(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é)有這么多單位可以看出選擇合適的單位可以讓人很直觀(guān)有個(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類(lèi)型,另外也方便我們?cè)诖a中作計(jì)算和比較,返回給用戶(hù)時(shí)則會(huì)轉(zhuǎn)成對(duì)用戶(hù)友好的單位,例如我們記錄用戶(hù)空間使用量在數(shù)據(jù)庫(kù)中會(huì)存儲(chǔ)最小單位Byte

用戶(hù)ID空間用量
183142212058073480
167652085350264853
151381439009188728
91521319605924042
240801259223266116
33251139222905087
94011128752330535
38381125023100502

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

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

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

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

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

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

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

  • 設(shè)置用戶(hù)最大容量接口
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ā)框架都使用SpringBoot,SpringBootJAVA對(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é)類(lèi)型再也不需要去做一大堆的格式轉(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)記在用戶(hù)的自定義注解上,那么這個(gè)用戶(hù)自定義注解上標(biāo)記的所有其他注解也會(huì)生效。

定義序列化器ByteFormatSerializer

ByteFormatSerializer類(lèi)的作用是當(dāng)Jackson序列化遇到Number類(lèi)型時(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類(lèi)的作用是Jackson反序列化遇到Number類(lèi)型時(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)換工具類(lèi)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è)接口返回用戶(hù)當(dāng)前使用容器量,然后把size大小設(shè)置成1572864,另一個(gè)是設(shè)置用戶(hù)最大使用容量,使用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);
    }

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

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

總結(jié)

本文使用Jackson自定義了ByteFormat注解,解決了字節(jié)類(lèi)型數(shù)據(jù)在前端與后端之間的優(yōu)雅轉(zhuǎn)換。當(dāng)然本方法不僅可以解決字節(jié)類(lèi)型的數(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ù)直接顯示就好,用戶(hù)輸入數(shù)據(jù)直接傳后端

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

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

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

相關(guān)文章

  • java如何利用poi解析doc和docx中的數(shù)據(jù)

    java如何利用poi解析doc和docx中的數(shù)據(jù)

    這篇文章主要給大家介紹了關(guān)于java如何利用poi解析doc和docx中數(shù)據(jù)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • 深入了解SpringBoot中@ControllerAdvice的介紹及三種用法

    深入了解SpringBoot中@ControllerAdvice的介紹及三種用法

    這篇文章主要為大家詳細(xì)介紹了SpringBoot中@ControllerAdvice的介紹及三種用法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-02-02
  • json-lib將json格式的字符串,轉(zhuǎn)化為java對(duì)象的實(shí)例

    json-lib將json格式的字符串,轉(zhuǎn)化為java對(duì)象的實(shí)例

    下面小編就為大家?guī)?lái)一篇json-lib將json格式的字符串,轉(zhuǎn)化為java對(duì)象的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-03-03
  • spring boot org.junit.jupiter.api不存在的解決

    spring boot org.junit.jupiter.api不存在的解決

    這篇文章主要介紹了spring boot org.junit.jupiter.api不存在的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Eclipse使用maven搭建spring mvc圖文教程

    Eclipse使用maven搭建spring mvc圖文教程

    這篇文章主要為大家分享了Eclipse使用maven搭建spring mvc圖文教程,感興趣的小伙伴們可以參考一下
    2016-05-05
  • Java Mybatis批量修改封裝詳解

    Java Mybatis批量修改封裝詳解

    這篇文章主要介紹了Mybatis批量修改封裝的相關(guān)內(nèi)容,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 詳解Java 微服務(wù)架構(gòu)

    詳解Java 微服務(wù)架構(gòu)

    這篇文章主要介紹了Java 微服務(wù)架構(gòu)的相關(guān)資料,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2021-02-02
  • JavaWeb文件上傳入門(mén)教程

    JavaWeb文件上傳入門(mén)教程

    這篇文章主要為大家詳細(xì)介紹了JavaWeb文件上傳入門(mén)教程,分析了文件上傳原理、介紹了第三方上傳組件,感興趣的小伙伴們可以參考一下
    2016-06-06
  • Java超詳細(xì)講解三大特性之一的多態(tài)

    Java超詳細(xì)講解三大特性之一的多態(tài)

    多態(tài)就是指程序中定義的引用變量所指向的具體類(lèi)型和通過(guò)該引用變量發(fā)出的方法調(diào)用在編程時(shí)并不確定,而是在程序運(yùn)行期間才確定,即一個(gè)引用變量到底會(huì)指向哪個(gè)類(lèi)的實(shí)例對(duì)象,該引用變量發(fā)出的方法調(diào)用到底是哪個(gè)類(lèi)中實(shí)現(xiàn)的方法,必須在由程序運(yùn)行期間才能決定
    2022-05-05
  • java入門(mén)概念個(gè)人理解之package與import淺析

    java入門(mén)概念個(gè)人理解之package與import淺析

    下面小編就為大家?guī)?lái)一篇java入門(mén)概念個(gè)人理解之package與import淺析。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-08-08

最新評(píng)論