参考String.substring() jdk1.6 vs jdk1.70差别,

本文对比了JDK6和JDK7中String.substring()方法的实现细节,揭示了在不同JDK版本下,该方法如何处理字符串子串的获取及潜在的性能问题。在JDK6中,substring()方法可能会导致不必要的内存占用,而在JDK7中,这一问题得到了改善。

参考String.substring() jdk1.6 vs jdk1.70差别,参数不合法时候通过异常来进行解决问题

1. substring()功能简介

String对象的substring(int beginIndex, int endIndex)方法返回此对象的一个子串,从beginIndex 开始,一直到 endIndex-1 结束,共 (endIndex - beginIndex)个字符。

新手提示: 

    1.1 String 的索引和数组一样,都是从0开始.

    1.2 注意,方法名字是substring(),全小写.

    1.3 有个重载方法是substring(int beginIndex),从beginIndex索引处开始,取得子字符串.

[java] view plaincopy

  1. String x = "abcdef";  

  2. int begin=1;  

  3. int end=3;  

  4. x = x.substring(begin, end);  

  5. System.out.println(x);  

 

执行结果(包含索引为 begin,直到 end-1 的字符):

[plain] view plaincopy

  1. bc  

 

2. 当substring()被调用时,发生了什么?

你应该知道,因为 x 是不可变的,当 指定 x 等于 x.substring(begin, end)时,实际上 x 指向了一个全新的字符串,如下图所示:

图1

 

 

然而,这幅图并不是完全正确的,堆内存中所真正发生的事也不是这么简单.那么,在JDK6和JDK7之间 substring()的调用到底有些什么区别呢?

 

3. JDK 6中的substring()方法

String实际上是一个字符数组.在 JDK6中, String对象主要包含3个属性域: 

[java] view plaincopy

  1. private final char value[];  

  2. private final int offset;  

  3. private final int count;  

 

他们用于存储实际的字符数组,数组的第一个索引,以及String的字符个数.

当调用 substring() 方法时,创建了一个新的String对象,但是string的value[] 属性域仍然指向堆内存中的原来的那个数组。区别就是 两个对象的 count 和 offset 这两个值不同了。 如下图所示:

图2

要解释这个问题,下面是最关键部分的代码:

[java] view plaincopy

  1. // JDK6,包级私有构造,共享 value数组提升速度  

  2. String(int offset, int count, char value[]) {  

  3.     this.value = value;  

  4.     this.offset = offset;  

  5.     this.count = count;  

  6. }  

  7.   

  8.   

  9. public String substring(int beginIndex, int endIndex) {  

  10.     // ... 检查边界的代码  

  11.     // 如果范围和自己一模一样,则返回自身,否则用value字符数组构造一个新的对象  

  12.     return ((beginIndex == 0) && (endIndex == count)) ? this :  

  13.         new String(offset + beginIndex, endIndex - beginIndex, value);  

  14. }  

 

4. JDK 6中substring()引起的问题

如果有一个"非常"长的字符串,但每次使用substring()时只想要很小的一部分,那么将会引起另一个性能问题: 虽然你只需要很小的一部分,但是持有了整个value[]的引用,从而导致大量内存被占用。

要解决这个问题,在JDK6中可以让其指向一个真正的子字符串,示例代码:

[java] view plaincopy

  1. x = x.substring(begin, end) + "";  

 

5. JDK 7中的substring()方法

在JDK 7 中这个问题得到改进, substring()方法真实地在堆内存中创建了另一个字符数组.

图3

 

[java] view plaincopy

  1. // JDK 7, 权限变为 public   

  2. public String(char value[], int offset, int count) {  

  3.     // ... 检查边界..  

  4.     // value 数组拷贝  

  5.     this.value = Arrays.copyOfRange(value, offset, offset+count);  

  6. }  

  7.   

  8.   

  9. public String substring(int beginIndex, int endIndex) {  

  10.     // ... 检查边界..  

  11.     int subLen = endIndex - beginIndex;  

  12.     // 如果和自身一样,那就返回自身,否则返回构造的新对象  

  13.     return ((beginIndex == 0) && (endIndex == value.length)) ? this  

  14.                 : new String(value, beginIndex, subLen);  

  15. }  

plugins { id 'com.android.application' } final def ISA_NAVI_INTERNAL_VERSION = getInternalVersionCode() android { compileSdkVersion 30 //buildToolsVersion "29.0.3" signingConfigs { releaseConfig { keyAlias 'android_box' keyPassword 'android_box' storeFile file(rootDir.getAbsolutePath() + '/KeyStore/platform.keystore') storePassword 'android_box' } debugConfig { keyAlias 'android_box' keyPassword 'android_box' storeFile file(rootDir.getAbsolutePath() + '/KeyStore/platform.keystore') storePassword 'android_box' } } defaultConfig { applicationId "com.isa.navi" minSdkVersion 27 targetSdkVersion 30 versionCode getVersionCode() versionName getVersionName() signingConfig signingConfigs.releaseConfig buildConfigField "String", "BUILD_TIMESTAMP", getDate() ndk { // abiFilters "arm64-v8a", "armeabi-v7a", "x86", "x86_64" abiFilters "armeabi-v7a" } } packagingOptions { // exclude 'lib/armeabi-v7a/libIsaEngineJni.so' exclude 'lib/armeabi-v7a/libupdate-sdk.so' exclude 'lib/armeabi-v7a/libnative-lib.so' } buildTypes { debug { shrinkResources false minifyEnabled false zipAlignEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' signingConfig signingConfigs.debugConfig buildConfigField("String", "ISA_NAVI_VERSION_INFO", '"DEBUG_' + ISA_NAVI_INTERNAL_VERSION + '; ' + getVersionName() + '; ' + getVersionCode() + '"') buildConfigField "int", "LOG_FLAG", "0" externalNativeBuild { cmake { // 设置 CMAKE_BUILD_TYPE arguments "-DCMAKE_BUILD_TYPE=Release" arguments "-DENABLE_VIEWER_TOOL=ON" } } } release_st { shrinkResources true minifyEnabled true zipAlignEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' signingConfig signingConfigs.releaseConfig buildConfigField("String", "ISA_NAVI_VERSION_INFO", '"RELEASE_ST_' + ISA_NAVI_INTERNAL_VERSION + '; ' + getVersionName() + '; ' + getVersionCode() + '"') buildConfigField "int", "LOG_FLAG", "1" externalNativeBuild { cmake { // 设置 CMAKE_BUILD_TYPE arguments "-DCMAKE_BUILD_TYPE=Release" arguments "-DENABLE_VIEWER_TOOL=ON" } } } release { shrinkResources true minifyEnabled true zipAlignEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' signingConfig signingConfigs.releaseConfig buildConfigField("String", "ISA_NAVI_VERSION_INFO", '"RELEASE_' + ISA_NAVI_INTERNAL_VERSION + '; ' + getVersionName() + '; ' + getVersionCode() + '"') buildConfigField "int", "LOG_FLAG", "2" externalNativeBuild { cmake { // 设置 CMAKE_BUILD_TYPE arguments "-DCMAKE_BUILD_TYPE=Release" arguments "-DENABLE_VIEWER_TOOL=ON" } } } release_sop { shrinkResources true minifyEnabled true zipAlignEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' signingConfig signingConfigs.releaseConfig buildConfigField("String", "ISA_NAVI_VERSION_INFO", '"RELEASE_SOP_' + ISA_NAVI_INTERNAL_VERSION + '; ' + getVersionName() + '; ' + getVersionCode() + '"') buildConfigField "int", "LOG_FLAG", "3" externalNativeBuild { cmake { // 设置 CMAKE_BUILD_TYPE arguments "-DCMAKE_BUILD_TYPE=Release" arguments "-DENABLE_VIEWER_TOOL=OFF" } } } } externalNativeBuild { cmake { path "../../CMakeLists.txt" // version "3.10.2" } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } android.applicationVariants.all { variant -> variant.outputs.all { //这里修改apk文件名 outputFileName = "S32_ISA_Navi.apk" } } sourceSets { main { // jniLibs.srcDirs = ["libs"] java.srcDirs = ['src/main/java'] aidl.srcDirs = ['src/main/aidl'] // assets.srcDirs = ['src/main/assets'] // manifest.srcFile 'src/main/AndroidManifest.xml' } } repositories { flatDir { dirs 'libs' } } ndkVersion '21.4.7075529' /*lintOptions { checkReleaseBuilds false }*/ } dependencies { implementation 'androidx.appcompat:appcompat:1.1.0' implementation files('libs/dfcar.jar') // implementation 'org.jetbrains:annotations:15.0' compileOnly files('libs/DFAPI.jar') compileOnly files('libs/framework.jar') implementation files('libs/dfcert_hal.jar') implementation project(':isa-navilib') implementation project(':isa-cplib') implementation 'com.google.code.gson:gson:2.8.6' implementation 'org.bouncycastle:bcprov-jdk15on:1.70' implementation 'org.bouncycastle:bcpkix-jdk15on:1.70' } static def getVersionCode() { // def cmd = 'git rev-list --count HEAD' def code = 1 // if (cmd.execute().text.length() > 0) { // try { // code = Integer.decode(cmd.execute().text.trim()) // } catch (Exception ignore) { // } // } println("getVersionCode -> " + code) code } static def getGitCommitDate() { // def cmd = 'git log --pretty=format:%at -1' //def gitCommit = cmd.execute().text.trim() + "000" def formattedDate = "2023-11-10"//new Date(Long.decode(gitCommit)).format('yyyy-MM-dd') println("getGitCommitDate -> " + formattedDate) formattedDate } static def getVersionName() { // def xml = new XmlSlurper().parse(new File("AndroidManifest.xml")) // xml.declareNamespace('android': 'http://schemas.android.com/apk/res/android') // return xml.application.@'android:versionName' return getGitCommitDate(); } static def getInternalVersionCode() { def result = 'UNKONWN' // tryitId.length() - 8).toUpperCase() // } catch (Exception { //// def cmd = 'git rev-list HEAD --max-count=1' //// def commitId = cmd.execute().text.trim() //// result = commitId.substring(comm ignore) { // } result } std::String getDate() { Date date = new Date(); String dates = "\""+date.format("yyyy-MM-dd hh:mm:ss", TimeZone.getTimeZone("UTC"))+"\""; return dates; }
最新发布
08-02
package cn.attackme.myuploader.controller; import cn.attackme.myuploader.service.impl.FileServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; /** * 大文件上传 */ @RestController @RequestMapping("/BigFile") @CrossOrigin public class BigFileUploadController { @Autowired private FileServiceImpl fileService; @PostMapping("/") public void upload(String name, String md5, Long size, Integer chunks, Integer chunk, MultipartFile file) throws IOException { if (chunks != null && chunks != 0) { fileService.uploadWithBlock(name, md5,size,chunks,chunk,file); } else { fileService.upload(name, md5,file); } } } package cn.attackme.myuploader.controller; import cn.attackme.myuploader.model.File; import cn.attackme.myuploader.model.OssFile; import cn.attackme.myuploader.minio.MinioProperties; import cn.attackme.myuploader.minio.MinioService; import cn.attackme.myuploader.minio.MinioTemplate; import cn.attackme.myuploader.service.impl.FileServiceImpl; import cn.attackme.myuploader.utils.CryptoUtil; import cn.attackme.myuploader.utils.RequestBean; import cn.attackme.myuploader.utils.Result; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.apache.commons.io.IOUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.crypto.SecretKey; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController @RequestMapping("/files") public class FileMinioController { // AES 密钥(必须为16字节,真实项目中建议从配置中获取或使用更安全的方式管理) private static final String ENCRYPTION_KEY = "0123456789abcdef"; @Autowired private MinioTemplate minioTemplate; // 自定义的上传封装类 @Autowired private MinioProperties minioProperties; @Autowired private MinioService minioService; // 用于获取公开 URL 或下载文件 @Autowired private FileServiceImpl fileService; /** * 上传文件:先加密后上传到 Minio */ @PostMapping("/upload") public Map<String, String> uploadFile(@RequestParam("file") MultipartFile file) { Map<String, String> resultMap = new HashMap<>(); try { // 读取文件内容 byte[] fileData = file.getBytes(); // 生成 AES 密钥 SecretKey secretKey = CryptoUtil.getSecretKey(ENCRYPTION_KEY); // 对文件数据进行加密 byte[] encryptedData = CryptoUtil.encrypt(fileData, secretKey); // 将加密后的数据转换为 InputStream 进行上传 ByteArrayInputStream encryptedInputStream = new ByteArrayInputStream(encryptedData); // 检查文件名格式(必须包含扩展名) String originalFilename = file.getOriginalFilename(); if (originalFilename == null || !originalFilename.contains(".")) { resultMap.put("code", "0001"); resultMap.put("msg", "无效的文件名"); return resultMap; } int dotIndex = originalFilename.lastIndexOf("."); String fileNameWithoutSuffix = originalFilename.substring(0, dotIndex); String suffix = originalFilename.substring(dotIndex + 1).toLowerCase(); // 可选:根据需求设置上传时的 contentType,此处直接使用上传文件的 contentType String contentType = file.getContentType(); // 调用 minioTemplate 上传文件,返回文件信息对象 OssFile OssFile ossFile = minioTemplate.upLoadFile( minioProperties.getBucketName(), minioProperties.getFolderName(), fileNameWithoutSuffix, suffix, encryptedInputStream, contentType ); System.out.println("文件的位置filePath:" + ossFile.getFilePath()); // 获取公开访问地址 String publicObjectUrl = minioService.getPublicObjectUrl(ossFile.getFilePath()); resultMap.put("code", "0000"); resultMap.put("msg", "上传成功"); resultMap.put("url", publicObjectUrl); return resultMap; } catch (Exception e) { // 建议使用日志记录完整异常,方便调试 e.printStackTrace(); resultMap.put("code", "0001"); resultMap.put("msg", "上传失败"); return resultMap; } } @PostMapping("/download") public void downloadFile(@RequestBody File file, HttpServletResponse response) { try { String filePath = file.getPath(); String md5 = file.getMd5(); // 1. 获取文件名 String fileName = filePath.substring(filePath.lastIndexOf("/") + 1); // 2. 从 MinIO 读取加密数据 InputStream inputStream = minioService.getObjectInputStream(filePath); byte[] encryptedData = IOUtils.toByteArray(inputStream); // 3. 如果 md5 为空,直接返回加密数据(不解密) if (md5 == null || md5.isEmpty()) { response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name())); response.setContentLength(encryptedData.length); ServletOutputStream outputStream = response.getOutputStream(); outputStream.write(encryptedData); outputStream.flush(); outputStream.close(); } // 4. md5 存在时,执行解密 SecretKey secretKey = CryptoUtil.getSecretKey(md5); byte[] decryptedData = CryptoUtil.decrypt(encryptedData, secretKey); // 5. 返回解密后的数据 response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name())); response.setContentLength(decryptedData.length); ServletOutputStream outputStream = response.getOutputStream(); outputStream.write(decryptedData); outputStream.flush(); outputStream.close(); } catch (Exception e) { response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); // 建议记录日志:log.error("文件下载失败", e); } } @GetMapping("/selectAll") public Result<List<File>> selectAll(RequestBean requestBean){ return fileService.selectAll(requestBean); } // 创建文件记录 @PostMapping public boolean create(@RequestBody File file) { return fileService.save(file); } // 删除文件记录 @DeleteMapping("/{id}") public boolean delete(@PathVariable Long id) { return fileService.removeById(id); } // 更新文件记录 @PutMapping public boolean update(@RequestBody File file) { return fileService.updateById(file); } // 根据ID获取文件记录 @GetMapping("/{id}") public File getById(@PathVariable Long id) { return fileService.getById(id); } // 获取所有文件记录 @GetMapping("/all") public List<File> listAll() { return fileService.list(); } // 以下是增强功能示例(根据需求可选添加) // 根据MD5查询文件(用于秒传校验) @GetMapping("/byMd5/{md5}") public File getByMd5(@PathVariable String md5) { return fileService.lambdaQuery() .eq(File::getMd5, md5) .one(); } // 分页查询(需配合PageHelper或MyBatis-Plus分页插件) @GetMapping("/page") public List<File> pageList(@RequestParam int pageNum, @RequestParam int pageSize) { return fileService.page(new Page<>(pageNum, pageSize)).getRecords(); } } package cn.attackme.myuploader.controller; import cn.attackme.myuploader.model.File; import cn.attackme.myuploader.service.impl.FileServiceImpl; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.util.List; /** * 文件上传 */ @RestController @RequestMapping("/File") @CrossOrigin public class FileUploadController { @Autowired private FileServiceImpl fileService; @PostMapping("/") public void upload(String name, String md5, MultipartFile file) throws IOException { fileService.upload(name, md5,file); } } package cn.attackme.myuploader.controller; import cn.attackme.myuploader.service.impl.FileServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 秒传 */ @RestController @RequestMapping("/QuickUpload") @CrossOrigin public class QuickUploadController { @Autowired private FileServiceImpl fileService; @GetMapping("/") public boolean upload(String md5) { return fileService.checkMd5(md5); } } package cn.attackme.myuploader.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import static cn.attackme.myuploader.utils.LogUtils.logToFile; /** * 测试日志功能 */ @RestController @RequestMapping("/Ex") public class TestExceptionController { /** * 测试日志切面 * @return */ @GetMapping("/aspect") public int aspect() { int i = 1 / 0; return i; } /** * 测试日志util */ @GetMapping("/util") public void util() { try { System.out.println(1/0); } catch (Exception e) { logToFile(e); } } } package cn.attackme.myuploader.controller; import cn.attackme.myuploader.model.UserSecret; import cn.attackme.myuploader.service.UserSecretService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/userSecrets") public class UserSecretController { @Autowired private UserSecretService userSecretService; @PostMapping public boolean create(@RequestBody UserSecret userSecret) { return userSecretService.save(userSecret); } @DeleteMapping("/{id}") public boolean delete(@PathVariable Long id) { return userSecretService.removeById(id); } @PutMapping public boolean update(@RequestBody UserSecret userSecret) { return userSecretService.updateById(userSecret); } @GetMapping("/{id}") public UserSecret getById(@PathVariable Long id) { return userSecretService.getById(id); } @GetMapping("/all") public List<UserSecret> listAll() { return userSecretService.list(); } } package cn.attackme.myuploader.model; import lombok.AllArgsConstructor; import lombok.Data; import lombok.ToString; import java.io.Serializable; import java.util.Date; /** * File表存储上传的文件信息 */ @Data @AllArgsConstructor @ToString public class File implements Serializable { private static final long serialVersionUID = -6956947981866795431L; private Long id; private String name; private String md5; private String path; private Date uploadTime; public File() { } public File(String name, String md5, String path, Date uploadTime) { this.name = name; this.md5 = md5; this.path = path; this.uploadTime = uploadTime; } } package cn.attackme.myuploader.model; import lombok.Data; import java.util.Date; /** * OssFile * * @author houzhaoran */ @Data public class OssFile { /** * 文件地址 */ private String filePath; /** * 域名地址 */ private String domain; /** * 文件名 */ private String name; /** * 原始文件名 */ private String originalName; /** * 文件hash值 */ public String hash; /** * 文件大小 */ private long size; /** * 文件上传时间 */ private Date putTime; /** * 文件contentType */ private String contentType; } <template> <div> <uploader browse_button="browse_button" :url="server_config.url+'/BigFile/'" chunk_size="100MB" :filters="{prevent_duplicates:true}" :FilesAdded="filesAdded" :BeforeUpload="beforeUpload" @inputUploader="inputUploader" /> <el-button type="primary" id="browse_button">选择多个文件</el-button> <br/> <el-table :data="tableData" style="width: 100%; margin: 10px 10px;"> <el-table-column label="文件名"> <template slot-scope="scope"> <span>{{scope.row.name}}</span> </template> </el-table-column> <el-table-column label="大小"> <template slot-scope="scope"> <span>{{scope.row.size}}</span> </template> </el-table-column> <el-table-column label="状态"> <template slot-scope="scope"> <span v-if="scope.row.status === -1">正在计算MD5</span> <span v-if="scope.row.status === 1">MD5计算完成,准备上传</span> <span v-if="scope.row.status === 4" style="color: brown">上传失败</span> <span v-if="scope.row.status === 5" style="color: chartreuse">已上传</span> <el-progress v-if="scope.row.status === 2" :text-inside="true" :stroke-width="20" :percentage="scope.row.percent"></el-progress> </template> </el-table-column> <el-table-column label="操作"> <template slot-scope="scope"> <el-button type="danger" @click="deleteFile(scope.row.id)">删除</el-button> </template> </el-table-column> </el-table> <br/> <el-button type="danger" @click="up.start()">开始上传</el-button> </div> </template> <script> import FileMd5 from '../models/file-md5.js' import Uploader from './Uploader' export default { name: 'BigFileUpload', data() { return { server_config: this.global.server_config, up: {}, files:[], tableData: [] } }, components: { 'uploader': Uploader }, watch: { files: { handler() { this.tableData = []; this.files.forEach((e) => { this.tableData.push({ name: e.name, size: e.size, status: e.status, id: e.id, percent: e.percent }); }); }, deep: true } }, methods: { inputUploader(up) { this.up = up; this.files = up.files; }, filesAdded(up, files) { files.forEach((f) => { f.status = -1; FileMd5(f.getNative(), (e, md5) => { f["md5"] = md5; f.status = 1; }); }); }, deleteFile(id) { let file = this.up.getFile(id); this.up.removeFile(file); }, beforeUpload(up, file) { up.setOption("multipart_params", {"size":file.size,"md5":file.md5}); } } } </script> <style scoped> </style> <template> <div> <uploader browse_button="browse_button" :url="server_config.url+'/BigFile/'" chunk_size="2MB" :filters="{prevent_duplicates:true}" :FilesAdded="filesAdded" :BeforeUpload="beforeUpload" @inputUploader="inputUploader" /> <el-button type="primary" id="browse_button">选择多个文件</el-button> <br/> <el-table :data="tableData" style="width: 100%; margin: 10px 10px;"> <el-table-column label="文件名"> <template slot-scope="scope"> <span>{{scope.row.name}}</span> </template> </el-table-column> <el-table-column label="大小"> <template slot-scope="scope"> <span>{{scope.row.size}}</span> </template> </el-table-column> <el-table-column label="状态"> <template slot-scope="scope"> <span v-if="scope.row.status === -1">正在计算MD5</span> <span v-if="scope.row.status === 1">MD5计算完成,准备上传</span> <span v-if="scope.row.status === 4" style="color: brown">上传失败</span> <span v-if="scope.row.status === 5 && scope.row.percent === 100" style="color: chartreuse">已上传</span> <span v-if="scope.row.status === 5 && scope.row.percent < 100" style="color: darkgreen">秒传成功</span> <el-progress v-if="scope.row.status === 2" :text-inside="true" :stroke-width="20" :percentage="scope.row.percent"></el-progress> </template> </el-table-column> <el-table-column label="操作"> <template slot-scope="scope"> <el-button type="danger" @click="deleteFile(scope.row.id)">删除</el-button> </template> </el-table-column> </el-table> <br/> <el-button type="danger" @click="uploadStart()">开始上传</el-button> </div> </template> <script> import FileMd5 from '../models/file-md5.js' import Uploader from './Uploader' export default { name: 'QuickUpload', data() { return { server_config: this.global.server_config, up: {}, files:[], tableData: [] } }, components: { 'uploader': Uploader }, watch: { files: { handler() { this.tableData = []; this.files.forEach((e) => { this.tableData.push({ name: e.name, size: e.size, status: e.status, id: e.id, percent: e.percent }); }); }, deep: true } }, methods: { inputUploader(up) { this.up = up; this.files = up.files; }, filesAdded(up, files) { files.forEach((f) => { f.status = -1; FileMd5(f.getNative(), (e, md5) => { f["md5"] = md5; f.status = 1; }); }); }, deleteFile(id) { let file = this.up.getFile(id); this.up.removeFile(file); }, beforeUpload(up, file) { up.setOption("multipart_params", {"size":file.size,"md5":file.md5}); }, uploadStart() { let count = 0, size = this.files.length; this.files.forEach((e) => { if (e.status == 1) { this.$http.get(this.server_config.url+'/QuickUpload/?md5='+e.md5) .then((response) => { count += 1; console.log(count); if (!response.data) { e.status = 5; } if (count == size){ this.up.start(); } }); } }); } } } </script> <style scoped> </style> <template> <div> <uploader browse_button="browse_button" :url="server_config.url+'/BigFile/'" chunk_size="2MB" :max_retries="3" :filters="{prevent_duplicates:true}" :FilesAdded="filesAdded" :BeforeUpload="beforeUpload" :Error="error" :UploadComplete="uploadComplete" @inputUploader="inputUploader" /> <el-tag type="warning">自动重传三次</el-tag> <br/> <br/> <el-button type="primary" id="browse_button">选择多个文件</el-button> <br/> <el-table :data="tableData" style="width: 100%; margin: 10px 10px;"> <el-table-column label="文件名"> <template slot-scope="scope"> <span>{{scope.row.name}}</span> </template> </el-table-column> <el-table-column label="大小"> <template slot-scope="scope"> <span>{{scope.row.size}}</span> </template> </el-table-column> <el-table-column label="状态"> <template slot-scope="scope"> <span v-if="scope.row.status === -1">正在计算MD5</span> <span v-if="scope.row.status === 1 && scope.row.percent === 0">MD5计算完成,准备上传</span> <span v-if="scope.row.status === 4" style="color: brown">上传失败</span> <span v-if="scope.row.status === 5" style="color: chartreuse">已上传</span> <el-progress v-if="scope.row.status === 2 || scope.row.status === 1 && scope.row.percent > 0" :text-inside="true" :stroke-width="20" :percentage="scope.row.percent"></el-progress> </template> </el-table-column> <el-table-column label="操作"> <template slot-scope="scope"> <el-button type="danger" @click="deleteFile(scope.row.id)">删除</el-button> </template> </el-table-column> </el-table> <br/> <el-button :disabled="uploading" type="danger" @click="uploadStart()">开始上传</el-button> <el-button :disabled="!uploading" type="warring" @click="uploadStop()">暂停上传</el-button> </div> </template> <script> import FileMd5 from '../models/file-md5.js' import Uploader from './Uploader' export default { name: "StopUpload", data() { return { server_config: this.global.server_config, up: {}, files:[], tableData: [], uploading: false } }, components: { 'uploader': Uploader }, watch: { files: { handler() { this.tableData = []; this.files.forEach((e) => { this.tableData.push({ name: e.name, size: e.size, status: e.status, id: e.id, percent: e.percent }); }); }, deep: true } }, methods: { inputUploader(up) { this.up = up; this.files = up.files; }, filesAdded(up, files) { files.forEach((f) => { f.status = -1; FileMd5(f.getNative(), (e, md5) => { f["md5"] = md5; f.status = 1; }); }); }, deleteFile(id) { let file = this.up.getFile(id); this.up.removeFile(file); }, beforeUpload(up, file) { up.setOption("multipart_params", {"size":file.size,"md5":file.md5}); }, uploadStart() { this.uploading = true; this.up.start(); }, uploadStop() { this.uploading = false; this.up.stop(); }, error() { this.uploading = false; }, uploadComplete() { this.uploading = false; } } } </script> <style scoped> </style> 请在上述代码的基础上利用Merkle tree算法对上传的文件进行完整性验证,并给出修改后的完整代码已经需要导入的包(尽量完整)
05-10
`string.Substring` 函数是 C# 中用于从字符串中提取子字符串的方法。它有两种重载形式: 1. `Substring(int startIndex)` 2. `Substring(int startIndex, int length)` ### 使用方法 1. **提取从指定位置到字符串结尾的子字符串** ```csharp string originalString = "Hello, World!"; string substring = originalString.Substring(7); Console.WriteLine(substring); // 输出: World! ``` 2. **提取从指定位置开始的指定长度的子字符串** ```csharp string originalString = "Hello, World!"; string substring = originalString.Substring(7, 5); Console.WriteLine(substring); // 输出: World ``` ### 注意事项 - **索引越界**:如果 `startIndex` 小于 0 或者大于字符串的长度,会抛出 `ArgumentOutOfRangeException` 异常。 - **长度越界**:如果 `length` 小于 0 或者 `startIndex + length` 大于字符串的长度,也会抛出 `ArgumentOutOfRangeException` 异常。 ### 示例代码 ```csharp using System; class Program { static void Main() { string originalString = "Hello, World!"; // 提取从指定位置到字符串结尾的子字符串 string substring1 = originalString.Substring(7); Console.WriteLine(substring1); // 输出: World! // 提取从指定位置开始的指定长度的子字符串 string substring2 = originalString.Substring(7, 5); Console.WriteLine(substring2); // 输出: World // 异常示例 try { string substring3 = originalString.Substring(14); } catch (ArgumentOutOfRangeException e) { Console.WriteLine(e.Message); // 输出: 索引和长度必须引用字符串内的位置。 } } } ``` ### 应用场景 - **字符串处理**:在处理字符串时,常常需要提取特定部分的子字符串,例如从完整的文件路径中提取文件名。 - **数据解析**:从复杂的字符串数据中提取有用的信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值