使用Node.js分片上传大文件到阿里云OSS

news2025/6/6 8:57:26

阿里云OSS的分片上传(Multipart Upload)是一种针对大文件优化的上传方式,其核心流程和关键特性如下:

1. ‌核心流程‌
分片上传分为三个步骤:

  • 初始化任务‌:调用InitiateMultipartUpload接口创建上传任务,获取唯一标识(Upload ID)。
  • 上传分片‌:通过UploadPart接口并发上传切分后的文件块(Part),每个Part需指定序号(partNumber),但上传顺序不影响最终合并。
  • 合并文件‌:调用CompleteMultipartUpload接口将所有Part按序号合并为完整文件。若中途放弃上传,需调用AbortMultipartUpload清理未完成的Part。

2. 适用场景‌

  • 大文件加速上传‌:文件超过5GB时,分片上传通过并行传输显著提升速度。
  • 网络环境波动‌:单个Part失败仅需重传该部分,避免全文件重传。
  • 暂停与恢复‌:任务无过期时间,可随时中断后继续上传。
  • 未知文件大小‌:如视频监控等场景,可在文件生成过程中开始上传。

3. 技术细节与建议‌

  • ‌分片大小‌:官方建议文件大于100MB时采用分片上传,过小分片可能导致进度显示异常。
  • 并发控制‌:并非并发数越多越快,需权衡网络带宽和设备负载。
  • ‌安全实践‌:浏览器端使用时应通过临时访问凭证(STS)授权,避免暴露主账号密钥。
  • ‌跨域配置‌:浏览器直接上传需预先设置OSS的CORS规则。

4. 操作方式‌

  • 工具支持‌:OSS控制台不支持分片上传,需使用ossbrowser(图形化工具)、ossutil(命令行)或SDK(如Browser.js、阿里云SDK)。
  • 代码示例‌:Vue项目中可通过ali-oss SDK实现分片上传,结合进度条组件(如Element UI)提升用户体验。

5. 注意事项‌

  • ‌碎片清理‌:未完成的分片会占用存储空间,需手动调用AbortMultipartUpload或配置生命周期规则自动清理。
  • 费用影响:未清理的分片会产生存储费用,每月费用 = 分片大小 × 分片数量 × 存储单价 × 存储天数。始终对放弃的上传调用
    abortMultipartUpload
  • 自动清理机制:OSS 默认 7 天后清理未完成的分片。可通过生命周期规则缩短:
xml:
<LifecycleConfiguration>
  <Rule>
    <AbortIncompleteMultipartUpload>
      <DaysAfterInitiation>1</DaysAfterInitiation> <!-- 1天后清理 -->
    </AbortIncompleteMultipartUpload>
  </Rule>
</LifecycleConfiguration>
  • API灵活性‌:若需高度定制化,可直接调用REST API(需自行计算签名)。
    在这里插入图片描述

一:阿里官方文档:Node.js用一个步骤实现分片上传
二:阿里官方文档:Node.js分三个步骤实现分片上传,可扩展暂停/继续上传和取消上传操作
在这里插入图片描述

一个步骤实现:

以上分片上传完整示例调用的方法multipartUpload中封装了初始化分片上传、上传分片以及完成分片上传三个API接口。如果您希望分步骤实现分片上传,请依次调用.initMultipartUpload、.uploadPart以及.completeMultipartUpload方法。
在这里插入图片描述
在这里插入图片描述

<template>
  <div class="upload-container">
    <el-upload ref="upload" drag action="" :auto-upload="false" :on-change="handleFileChange" :limit="1" :on-exceed="handleExceed">
      <i class="el-icon-upload"></i>
      <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
    </el-upload>
    <el-button style="margin-top: 20px;" type="primary" @click="uploadFile" :disabled="!isupload">开始上传</el-button>
    <el-progress v-if="uploading" :text-inside="true" :stroke-width="26" :percentage="uploadPercentage"></el-progress>
  </div>
</template>

<script>
import OSS from 'ali-oss'; // npm install ali-oss
import md5 from 'js-md5'; // npm install js-md5
export default {
  data() {
    return {
      file: null,
      uploading: false,
      isupload: false,
      uploadPercentage: 0,
    };
  },
  methods: {
    handleFileChange(file) {
      this.file = file.raw;
      this.isupload = true;
    },
    handleExceed() {
      this.$message.warning('只能上传一个文件');
    },
    async uploadFile() {
      if (!this.file) {
        this.$message.error('请先选择文件');
        return;
      }

      this.uploading = true;
      try {
        let content = this.setConfig(this.$ossConfig);//这里涉及身份识别,我这里是取接口返回在解密获得(如上图),下同,不再解释
        console.log(content)
        const client = new OSS({
          region: content.region,
          bucket: content.bucket,
          accessKeyId: content.accessKeyId,
          accessKeySecret: content.accessKeySecret,
          stsToken: content.securityToken,
        });

        const fileName = `uploads/${this.file.name}`;
        const options = {
          partSize: 1024 * 1024 * 5, // 分片大小为 5MB
          parallel: 3, // 并发上传的分片数量
          progress: (p, cpt, res) => {
            this.uploadPercentage = Math.round(p * 100);
          },
        };

        const result = await client.multipartUpload(fileName, this.file, options);
        this.$message.success('文件上传成功');
        console.log('Upload result:', result);
      } catch (error) {
        this.$message.error('文件上传失败');
        console.error('Upload error:', error);
      } finally {
        this.uploading = false;
        this.uploadPercentage = 0;
      }
    },
    setConfig(content) {
      let Base64 = require('js-base64').Base64
      let str1 = Base64.decode(content.substr(1, 3) + content.substr(5, 3) + content.substr(9, content.length - 9));
      let contentN = Base64.decode(str1.substr(1, 3) + str1.substr(5, 3) + str1.substr(9, str1.length - 9));
      return JSON.parse(contentN);
    },

  },
};
</script>

<style scoped>
.upload-container {
  max-width: 600px;
  margin: 20px auto;
  padding: 20px;
  border: 1px solid #ebeef5;
  border-radius: 4px;
}
</style>

效果:
在这里插入图片描述
在这里插入图片描述

扩展:一个步骤扩展暂停/继续上传、取消上传

<template>
  <div class="upload-container">

    <!-- <el-upload ref="uploadRef" class="upload-demo" drag :action="''" :auto-upload="false" :show-file-list="false" accept="*" :limit="2" :on-change="handleChange" :on-exceed="handleExceed"> -->
    <el-upload ref="uploadRef" class="upload-demo" drag :action="''" :auto-upload="true" :show-file-list="false" accept="*" :limit="1" :before-upload="handleChange">
      <i class="el-icon-upload"></i>
      <div class="el-upload__text">将文件拖到此处,或<em>点击上传3</em></div>
    </el-upload>

    <div v-if="file" class="file-info">
      <div>文件名称: {{ file.name }}</div>
      <div>文件大小: {{ formatFileSize(file.size) }}</div>
      <div>分片大小: {{ (chunkSize / 1024 / 1024).toFixed(2) }} MB</div>
      <div>分片数量: {{ chunks }}</div>
    </div>

    <el-progress v-if="showProgress && !isUploaded" :percentage="percentage" :status="uploadStatus" :stroke-width="16" style="margin: 20px 0"></el-progress>
    <el-button v-if="isUploaded" type="text">上传成功</el-button>

    <div v-if="showButtons">
      <!-- <el-button type="primary" @click="submitForm" v-if="!isUploading && !isUploaded && !tempCheckpoint">开始上传</el-button> -->
      <el-button type="primary" icon="el-icon-refresh-left" @click="submitForm" v-if="!isUploading && !isUploaded && !tempCheckpoint">重试</el-button>

      <el-button type="text" @click="shareclearn" v-if="isUploaded">分享 | 清除记录</el-button>

      <el-button type="primary" icon="el-icon-video-pause" @click="pauseUpload()" v-if="paused && isUploading && !isUploaded">暂停上传</el-button>
      <el-button type="primary" icon="el-icon-video-play" @click="resumeUpload()" v-if="!paused && !isUploaded">继续上传</el-button>

      <el-button type="danger" icon="el-icon-circle-close" @click="cancelUpload" v-if="!isUploaded && tempCheckpoint">取消上传</el-button>
    </div>

  </div>
</template>

<script>
import OSS from 'ali-oss'
import md5 from 'js-md5'

export default {
  data() {
    return {
      client: null,
      parallel: 3, // 并发上传的分片数量
      chunkSize: 2 * 1024 * 1024, // 分片大小 2MB
      chunks: 0,// 总分片数量

      file: null,
      ossKey: '',

      uploadStatus: null, // 进度条上传状态
      percentage: 0, // 进度条百分比
      showProgress: false,// 是否显示进度条

      showButtons: false,// 是否显示按钮
      tempCheckpoint: null, // 用于缓存当前切片内容
      uploadId: '',
      isUploading: false,// 是否上传中
      isUploaded: false,//是否上传完毕
      paused: true,//是否暂停
    };
  },
  created() {
    this.initOSSClient();
  },
  methods: {
    async initOSSClient() {
      try {
        let content = this.setConfig(this.$ossConfig);
        console.log(content)
        this.client = new OSS({
          region: content.region,
          bucket: content.bucket,
          accessKeyId: content.accessKeyId,
          accessKeySecret: content.accessKeySecret,
          stsToken: content.securityToken,
        });

      } catch (error) {
        console.error('OSS初始化失败:', error);
        this.$message.error('上传服务初始化失败');
      }
    },
    setConfig(content) {
      let Base64 = require('js-base64').Base64
      let str1 = Base64.decode(content.substr(1, 3) + content.substr(5, 3) + content.substr(9, content.length - 9));
      let contentN = Base64.decode(str1.substr(1, 3) + str1.substr(5, 3) + str1.substr(9, str1.length - 9));
      return JSON.parse(contentN);
    },




    // 选择文件
    async handleChange(file) {
      // console.log(file)
      this.resetState();
      this.file = file//.raw;
      this.ossKey = `uploads/${Date.now()}_${this.file.name}`;
      this.chunks = Math.ceil(this.file.size / this.chunkSize);
      this.showButtons = true;
      this.submitForm();
    },
    // 开始上传
    submitForm() {
      this.initMultipartUpload();
      this.multipartUpload();
    },
    // 初始化分片上传
    async initMultipartUpload() {
      const result = await this.client.initMultipartUpload(this.ossKey);
      this.uploadId = result.uploadId;
      console.log(`初始化分片上传成功, uploadId: ${this.uploadId}`);
    },
    // 开始分片上传
    async multipartUpload() {
      if (!this.file) {
        this.$message.error('请选择文件');
        return;
      }
      this.uploadStatus = null;
      this.percentage = 0;
      this.showProgress = true;
      this.isUploading = true;
      try {
        const result = await this.client.multipartUpload(this.file.name, this.file, {
          progress: (p, checkpoint) => {
            this.tempCheckpoint = checkpoint;
            // this.uploadId = checkpoint.uploadId;
            this.percentage = Math.floor(p * 100);
          },
          parallel: this.parallel,
          partSize: this.chunkSize,
        });
        this.uploadStatus = 'success';
        this.isUploading = false;
        this.isUploaded = true;
        this.$message.success('上传成功');
        console.log(result, '切片上传完毕');
      } catch (e) {
        if (e.code === 'ConnectionTimeoutError') {
          this.uploadStatus = 'exception';
          window.addEventListener('online', this.resumeUpload);
        }
        this.isUploading = false;
        console.error(e);
      }
    },

    // 取消上传
    cancelUpload() {
      this.pauseUpload();
      this.paused = true;
      this.showProgress = false;
      this.percentage = 0;
      this.uploadStatus = null;
      this.tempCheckpoint = null;
      this.uploadId = '';
    },

    // 暂停上传
    pauseUpload() {
      this.isUploading = false;
      this.paused = false;
      this.client.cancel();
    },
    // 继续上传
    async resumeUpload() {
      if (!this.tempCheckpoint) {
        this.$message.error('请先上传');
        return;
      }
      this.uploadStatus = null;
      this.isUploading = true;
      this.paused = true;
      try {
        const result = await this.client.multipartUpload(this.file.name, this.file, {
          progress: (p) => {
            this.percentage = Math.floor(p * 100);
          },
          checkpoint: this.tempCheckpoint,
        });
        this.uploadStatus = 'success';
        this.isUploading = false;
        this.isUploaded = true;
        this.$message.success('续传成功');
        console.log(result, '恢复上传完毕');
      } catch (e) {
        this.isUploading = false;
        console.error(e);
      }
    },


    // 重置
    resetState() {
      this.file = null;
      this.ossKey = '';

      this.uploadStatus = null;
      this.percentage = 0;
      this.showProgress = false;

      this.showButtons = false;
      this.tempCheckpoint = null;
      this.uploadId = '';
      this.isUploading = false;
      this.isUploaded = false;
      this.paused = true;
    },
    // 限制上传一个文件,重新选择文件替换原来的文件
    handleExceed(files) {
      // console.log(this.$refs.uploadRef)
      if (this.$refs.uploadRef.uploadFiles.length > 1) {
        this.$refs.uploadRef.uploadFiles.shift();
      }
    },
    // 格式化文件大小
    formatFileSize(size) {
      const units = ['B', 'KB', 'MB', 'GB'];
      let unitIndex = 0;
      while (size >= 1024 && unitIndex < units.length - 1) {
        size /= 1024;
        unitIndex++;
      }
      return `${size.toFixed(2)} ${units[unitIndex]}`;
    },

    shareclearn() {
      console.log('分享 | 清除记录')
    },

  },
};
</script>

<style scoped>
.upload-container {
  width: 100%;
  max-width: 600px;
  margin: 0 auto;
}

.el-button {
  margin-right: 10px;
}

.file-info {
  margin: 20px 0;
  padding: 15px;
  background-color: #f8f9fa;
  border-radius: 4px;
}

.file-info div {
  margin-bottom: 10px;
  color: #606266;
}
</style>

效果:

一个步骤分片上传(扩展暂停继续取消)

三个步骤实现:

<template>
  <div class="upload-container">
    <!-- multiple drag on-change -->
    <el-upload class="upload-demo" action="" :show-file-list="false" :auto-upload="true" :before-upload="handleFileChange" :accept="'*'">
      <!-- <i class="el-icon-upload"></i>
      <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div> -->
      <el-button size="small" type="primary">选择文件</el-button>
    </el-upload>

    <div class="file-info" v-if="file">
      <div>文件名: {{ file.name }}</div>
      <div>文件大小: {{ formatFileSize(file.size) }}</div>
      <div>分片大小: {{ (chunkSize / 1024 / 1024).toFixed(2) }} MB</div>
      <div>分片数量: {{ chunks }}</div>

      <el-progress v-if="isUploading" :percentage="progressPercent" :status="uploadStatus" :stroke-width="14"></el-progress>
      <div>
        <el-button type="primary" :icon="isPaused?'el-icon-video-play':'el-icon-video-pause'" @click="togglePause" v-if="isUploading">{{ isPaused ? '继续' : '暂停' }}</el-button>
        <el-button type="danger" icon="el-icon-circle-close" @click="cancelUpload" v-if="isUploading">取消</el-button>
        <el-button type="primary" icon="el-icon-refresh-left" @click="startUpload" v-if="!isUploading && !uploaded">重试</el-button>
        <el-button type="text" v-if="!isUploading && uploaded">分享 | 清除记录</el-button>
      </div>
    </div>
  </div>
</template>

<script>
import OSS from 'ali-oss'
import md5 from 'js-md5'

export default {
  data() {
    return {
      file: null,
      chunkSize: 2 * 1024 * 1024, // 5MB 分片
      chunks: 0,                  // 分片数量

      uploadedChunks: [],  // 已上传的分片索引
      uploadId: '',        // OSS 分片上传 ID
      client: null,        // OSS 客户端
      isPaused: false,     // 是否暂停
      isUploading: false,  // 上传中
      uploaded: false,     // 上传完成

      progressPercent: 0,  // 进度条-百分比
      uploadStatus: null,  // 进度条-状态

      concurrency: 3,      // 并发数
      activeChunks: 0,     // 当前活跃的分片数
      cancelTokens: {},    // 取消令牌
      parts: [],           // 分片列表
    };
  },
  created() {
    this.initOSSClient();
  },
  methods: {
    async initOSSClient() {
      // 这里应该从后端获取临时凭证(安全考虑)
      // 以下是模拟数据,实际使用需替换为真实接口
      const credentials = await this.getSTSToken();
      this.client = new OSS({
        region: credentials.region,
        accessKeyId: credentials.accessKeyId,
        accessKeySecret: credentials.accessKeySecret,
        stsToken: credentials.securityToken,
        bucket: credentials.bucket,
        refreshSTSToken: async () => {
          const newCredentials = await this.getSTSToken();
          return {
            accessKeyId: newCredentials.accessKeyId,
            accessKeySecret: newCredentials.accessKeySecret,
            stsToken: newCredentials.securityToken
          };
        }
      });
    },
    async getSTSToken() {
      // 实际项目中从这里获取STS token
      // 示例返回结构:
      var content = this.setConfig(this.$ossConfig);
      console.log(content)
      return {
        region: content.region,
        bucket: content.bucket,
        accessKeyId: content.accessKeyId,
        accessKeySecret: content.accessKeySecret,
        securityToken: content.securityToken,
      };
    },
    setConfig(content) {
      let Base64 = require('js-base64').Base64
      let str1 = Base64.decode(content.substr(1, 3) + content.substr(5, 3) + content.substr(9, content.length - 9));
      let contentN = Base64.decode(str1.substr(1, 3) + str1.substr(5, 3) + str1.substr(9, str1.length - 9));
      return JSON.parse(contentN);
    },

    // 格式化文件大小
    formatFileSize(size) {
      const units = ['B', 'KB', 'MB', 'GB'];
      let unitIndex = 0;
      while (size >= 1024 && unitIndex < units.length - 1) {
        size /= 1024;
        unitIndex++;
      }
      return `${size.toFixed(2)} ${units[unitIndex]}`;
    },
    // 标准化ETag格式
    normalizeETag(etag) {
      let cleanETag = String(etag)
        .replace(/^"+/, '')
        .replace(/"+$/, '')
        .replace(/\\/g, '');

      if (!/^[0-9A-F]{32}$/i.test(cleanETag)) {
        throw new Error(`无效的ETag格式: ${etag}`);
      }
      return `"${cleanETag}"`;
    },
    // 重置上传
    resetState() {
      this.uploadedChunks = [];
      this.uploadId = '';

      this.isPaused = false;
      this.isUploading = false;
      this.uploaded = false;

      this.progressPercent = 0;
      this.uploadStatus = null;

      this.activeChunks = 0;
      this.cancelTokens = {};


      // // 取消所有进行中的请求
      // Object.values(this.cancelTokens).forEach(ctrl => ctrl.abort());
      // this.cancelTokens = {};

      // // 强制更新视图
      // this.$nextTick();

    },

    handleFileChange(file) {
      // console.log(file)
      this.resetState();
      this.file = file;//file.raw;
      this.chunks = Math.ceil(this.file.size / this.chunkSize);
      this.startUpload();
    },

    // 1、初始化分片上传
    async startUpload() {
      if (!this.file) return;

      try {
        this.isUploading = true;
        this.isPaused = false;

        if (!this.uploadId) {
          const result = await this.client.initMultipartUpload(
            this.file.name,
            { timeout: 60000 }
          );
          this.uploadId = result.uploadId;
          console.log(`初始化分片上传成功, uploadId: ${this.uploadId}`);
        }



        // 获取未上传的分片
        const unuploaded = Array.from({ length: this.chunks }, (_, i) => i)
          .filter(i => !this.uploadedChunks.includes(i));

        // 并发控制
        const uploadNext = async () => {
          if (this.isPaused || !this.isUploading) return;

          // 上传完,合并分片
          if (unuploaded.length === 0 && this.activeChunks === 0) {
            await this.completeUpload();
            return;
          }

          if (this.activeChunks < this.concurrency && unuploaded.length > 0) {
            const chunkIndex = unuploaded.shift();
            this.activeChunks++;

            // 取消步骤一:创建取消令牌,取消正在进行的 HTTP 请求(分片上传请求)
            const controller = new AbortController();
            this.cancelTokens[chunkIndex] = controller;


            // 2、调用上传分片-s
            this.uploadChunk(chunkIndex, controller.signal)
              .then((res) => {
                // console.log(res)
                // console.log(chunkIndex)
                // this.parts.push({ partNumber: chunkIndex + 1, etag: res.etag });
                this.uploadedChunks.push(chunkIndex);
                this.updateProgress();
              })
              .catch(err => {
                if (err.name !== 'AbortError') {
                  console.error(`分片 ${chunkIndex} 上传失败:`, err);
                }
              })
              .finally(() => {
                this.activeChunks--;
                delete this.cancelTokens[chunkIndex];
                uploadNext();
              });
            // 调用上传分片-e


            uploadNext();
          }
        };

        // 启动并发上传
        for (let i = 0; i < this.concurrency; i++) {
          uploadNext();
        }

      } catch (err) {
        console.error('上传初始化失败:', err);
        this.$message.error('上传初始化失败');
        this.resetState();
      }
    },

    // 2、分片上传
    async uploadChunk(index, signal) {
      const start = index * this.chunkSize;
      const end = Math.min(start + this.chunkSize, this.file.size);
      const chunk = this.file.slice(start, end);

      return this.client.uploadPart(
        this.file.name,
        this.uploadId,
        index + 1,
        chunk,
        signal,
      );
    },

    // 3、完成上传
    async completeUpload() {
      try {
        // 获取已上传分片列表
        const listParts = await this.client.listParts(
          this.file.name,
          this.uploadId,
        );
        // console.log(listParts)

        // 按分片号排序
        const sortedParts = listParts.parts.sort((a, b) => a.partNumber - b.partNumber);
        // console.log(sortedParts)

        this.parts = await sortedParts.map(p => ({
          number: Number(p.PartNumber),//!!!注意!!!键名只能用number
          etag: this.normalizeETag(p.ETag),//!!!注意!!!键名只能用etag
        }))
        // console.log(this.parts)

        // 完成上传
        const completeResult = await this.client.completeMultipartUpload(
          this.file.name,
          this.uploadId,
          this.parts,
        );
        console.log(completeResult)

        this.$message.success('完成上传-成功');
        this.uploadStatus = 'success';
        this.uploaded = true;
        this.isUploading = false;
      } catch (err) {
        this.uploadStatus = 'exception';
        console.error('完成上传-失败:', err);
        this.$message.error('完成上传-失败');
      }
    },


    // 暂停/继续 上传
    togglePause() {
      this.isPaused = !this.isPaused;
      if (!this.isPaused) {
        this.startUpload();
      }
    },

    // 取消上传 
    cancelUpload() {
      this.isUploading = false;

      // 取消所有正在上传的分片
      Object.values(this.cancelTokens).forEach(controller => {
        controller.abort();
      });

      // 取消步骤二:清理OSS上传记录,清理掉OSS上存储的所有已经上传的分片,终止本次分片上传过程,并删除所有已上传的分片数据。
      // 这样确保不会在 OSS 上留下无效的分片数据,避免产生不必要的存储费用。
      if (this.uploadId && this.file) {
        try {
          this.client.abortMultipartUpload(this.file.name, this.uploadId)
            .finally(() => {
              this.resetState();
            })
            .catch((err) => {
              console.error('取消上传失败:', err)
            });
        } catch (err) {
          console.error('OSS清理异常:', err);
        }
      }

      this.$message.info('上传已取消');

      // 使用Vue的强制更新视图(针对Element UI进度条)
      this.$nextTick(() => {
        this.$forceUpdate();
      });
    },

    // 更新进度条
    updateProgress() {
      // 如果不在上传状态,不更新进度
      if (!this.isUploading) return;

      const percent = Math.round(
        (this.uploadedChunks.length / this.chunks) * 100
      );
      this.progressPercent = Math.min(percent, 100);
    },



  }
};
</script>

<style scoped>
.upload-container {
  max-width: 600px;
  margin: 20px auto;
  padding: 20px;
  border: 1px solid #ebeef5;
  border-radius: 4px;
}
.upload-demo {
  width: 300px;
  margin: 0 auto;
}
.file-info {
  margin-top: 20px;
  padding: 15px;
  background-color: #f8f9fa;
  border-radius: 4px;
}
.file-info div {
  margin-bottom: 10px;
  color: #606266;
}
.el-progress {
  margin-top: 20px;
}
</style>

效果:

三个步骤分片上传

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2401468.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

复变函数中的对数函数及其MATLAB演示

复变函数中的对数函数及其MATLAB演示 引言 在实变函数中&#xff0c;对数函数 ln ⁡ x \ln x lnx定义在正实数集上&#xff0c;是一个相对简单的概念。然而&#xff0c;当我们进入复变函数领域时&#xff0c;对数函数展现出更加丰富和复杂的性质。本文将介绍复变函数中对数函…

【Linux】Linux程序地址基础

参考博客&#xff1a;https://blog.csdn.net/sjsjnsjnn/article/details/125533127 一、地址空间的阐述 1.1 程序地址空间 下面的图片展示了程序地址空间的组成结构 我们通过代码来验证一下 int g_unval; int g_val 100;int main(int argc, char *argv[]);void test1() {i…

将图形可视化工具的 Python 脚本打包为 Windows 应用程序

前文我们已经写了一个基于python的tkinter库和matplotlib库的图形可视化工具。 基于Python的tkinter库的图形可视化工具&#xff08;15种图形的完整代码&#xff09;:基于Python的tkinter库的图形可视化工具&#xff08;15种图形的完整代码&#xff09;-CSDN博客 在前文基础上&…

无人机军用与民用技术对比分析

一、材料区别 军用无人机&#xff1a; 1. 高强度特种材料&#xff1a; 大量使用钛合金、碳纤维复合材料&#xff0c;兼顾轻量化与高强度&#xff0c;提升抗冲击性和隐身性能。 关键部件依赖进口材料。 2. 隐身涂层&#xff1a; 采用雷达吸波材料和低红外特征涂料&#xf…

刷leetcode hot100--矩阵6/1

1.螺旋矩阵【很久】6/1【感觉就是思路的搬运工&#xff0c;没完全理解】 54. 螺旋矩阵 - 力扣&#xff08;LeetCode&#xff09; 原来想 但是如果是奇数矩阵&#xff0c;遍历不到中间 解决思路&#xff1a; 用left,right,top,down标记/限定每次遍历的元素&#xff0c;每次从…

Docker轻松搭建Neo4j+APOC环境

Docker轻松搭建Neo4jAPOC环境 一、简介二、Docker部署neo4j三、Docker安装APOC插件四、删除数据库/切换数据库 一、简介 Neo4j 是一款高性能的 原生图数据库&#xff0c;采用 属性图模型 存储数据&#xff0c;支持 Cypher查询语言&#xff0c;适用于复杂关系数据的存储和分析。…

定制开发开源AI智能名片S2B2C商城小程序在无界零售中的应用与行业智能升级示范研究

摘要&#xff1a;本文聚焦无界零售背景下京东从零售产品提供者向零售基础设施提供者的转变&#xff0c;探讨定制开发开源AI智能名片S2B2C商城小程序在这一转变中的应用。通过分析该小程序在商业运营成本降低、效率提升、用户体验优化等方面的作用&#xff0c;以及其与京东AI和冯…

【大模型:知识图谱】--5.neo4j数据库管理(cypher语法2)

目录 1.节点语法 1.1.CREATE--创建节点 1.2.MATCH--查询节点 1.3.RETURN--返回节点 1.4.WHERE--过滤节点 2.关系语法 2.1.创建关系 2.2.查询关系 3.删除语法 3.1.DELETE 删除 3.2.REMOVE 删除 4.功能补充 4.1.SET &#xff08;添加属性&#xff09; 4.2.NULL 值 …

贪心算法应用:装箱问题(BFD算法)详解

贪心算法应用&#xff1a;装箱问题(BFD算法)详解 1. 装箱问题与BFD算法概述 1.1 装箱问题定义 装箱问题(Bin Packing Problem)是组合优化中的经典问题&#xff0c;其定义为&#xff1a; 给定n个物品&#xff0c;每个物品有大小wᵢ (0 < wᵢ ≤ C)无限数量的箱子&#xf…

编程技能:格式化打印05,格式控制符

专栏导航 本节文章分别属于《Win32 学习笔记》和《MFC 学习笔记》两个专栏&#xff0c;故划分为两个专栏导航。读者可以自行选择前往哪个专栏。 &#xff08;一&#xff09;WIn32 专栏导航 上一篇&#xff1a;编程技能&#xff1a;格式化打印04&#xff0c;sprintf 回到目录…

MPLAB X IDE ​软件安装与卸载

1、下载MPLAB X IDE V6.25 MPLAB X IDE | Microchip Technology 正常选Windows&#xff0c;点击Download&#xff0c;等待自动下载完成&#xff1b; MPLAB X IDE 一台电脑上可以安装多个版本&#xff1b; 2、安装MPLAB X IDE V6.25 右键以管理员运行&#xff1b;next; 勾选 I a…

windows编程实现文件拷贝

项目源码链接&#xff1a; 实现文件拷贝功能&#xff08;限制5GB大小&#xff09; 81c57de 周不才/cpp_linux study - Gitee.com 知识准备&#xff1a; 1.句柄 句柄是一个用于标识和引用系统资源&#xff08;如文件、窗口、进程、线程、位图等&#xff09;的值。它不是资…

[6-01-01].第12节:字节码文件内容 - 属性表集合

JVM学习大纲 二、属性表集合&#xff1a; 2.1.属性计数器&#xff1a; 2.2.属性表&#xff1a; 2.3.字节码文件组成5 -> 属性&#xff1a; 1.属性主要指的是类的属性&#xff0c;比如源码的文件名、内部类的列表等 2.4.字节码文件组成3 -> 字段&#xff1a; 1.字段中…

基于机器学习的水量智能调度研究

摘要&#xff1a;随着城市化进程的加速和水资源供需矛盾的日益突出&#xff0c;传统的水量调度模式因缺乏精准预测和动态调控能力&#xff0c;难以满足现代供水系统对高效性、稳定性和节能性的要求。本文针对供水系统中用水峰谷预测不准确、能耗高、供需失衡等核心问题&#xf…

深入浅出 Scrapy:打造高效、强大的 Python 网络爬虫

在数据为王的时代,高效获取网络信息是开发者必备的技能。今天我将为大家介绍 Python 爬虫领域的王者框架——Scrapy。无论你是数据工程师、分析师还是开发者,掌握 Scrapy 都能让你的数据采集效率提升数倍! 项目地址:https://github.com/scrapy/scrapy 官方文档:https://do…

贪心算法应用:带权任务间隔调度问题详解

贪心算法应用&#xff1a;带权任务间隔调度问题详解 贪心算法是一种在每一步选择中都采取在当前状态下最好或最优&#xff08;即最有利&#xff09;的选择&#xff0c;从而希望导致结果是全局最好或最优的算法。带权任务间隔调度问题是贪心算法的一个经典应用场景。 问题定义…

用电脑控制keysight示波器

KEYSIGHT示波器HD304MSO性能 亮点&#xff1a; 体验 200 MHz 至 1 GHz 的带宽和 4 个模拟通道。与 12 位 ADC 相比&#xff0c;使用 14 位模数转换器 &#xff08;ADC&#xff09; 将垂直分辨率提高四倍。使用 10.1 英寸电容式触摸屏轻松查看和分析您的信号。捕获 50 μVRMS …

LLaMA-Factory - 批量推理(inference)的脚本

scripts/vllm_infer.py 是 LLaMA-Factory 团队用于批量推理&#xff08;inference&#xff09;的脚本&#xff0c;基于 vLLM 引擎&#xff0c;支持高效的并行推理。它可以对一个数据集批量生成模型输出&#xff0c;并保存为 JSONL 文件&#xff0c;适合大规模评测和自动化测试。…

【Elasticsearch】Elasticsearch 核心技术(二):映射

Elasticsearch 核心技术&#xff08;二&#xff09;&#xff1a;映射 1.什么是映射&#xff08;Mapping&#xff09;1.1 元字段&#xff08;Meta-Fields&#xff09;1.2 数据类型 vs 映射类型1.2.1 数据类型1.2.2 映射类型 2.实际运用案例案例 1&#xff1a;电商产品索引映射案…

【计算机网络】网络层协议

1. ICMP协议的介绍及应用 IP协议的助手 —— ICMP 协议 ping 是基于 ICMP 协议工作的&#xff0c;所以要明白 ping 的工作&#xff0c;首先我们先来熟悉 ICMP 协议。 ICMP 全称是 Internet Control Message Protocol&#xff0c;也就是互联网控制报文协议。 里面有个关键词 …