背景

ollama 模型和相关配置文件默认都放在 models 文件夹下,想要把指定模型迁移到其他电脑比较麻烦,所以就有了该工具。还有就是模型下载本身就慢,一次下载多台使用减少下载次数。最重要的是公司电脑下载快,家里下载慢,在公司下载拷贝回家用

如何使用

下载地址

命令

ollamab 模型名称+型号(和 ollama 名称列表一致) 输出路径 (可选)

ollamab.exe qwen:0.5b D:/

执行结果

D:\code\private\model-backup>ollamab.exe qwen:0.5b
模型路径: D:\ai\.ollama\models
开始打包,耐心等待…………
添加到 ZIP 中: blobs\sha256-fad2a06e4cc705c2fa8bec5477ddb00dc0c859ac184c34dcc5586663774161ca
添加到 ZIP 中: blobs\sha256-41c2cf8c272f6fb0080a97cd9d9bd7d4604072b80a0b10e7d65ca26ef5000c0c
添加到 ZIP 中: blobs\sha256-1da0581fd4ce92dcf5a66b1da737cf215d8dcf25aa1b98b44443aaf7173155f5
添加到 ZIP 中: blobs\sha256-f02dd72bb2423204352eabc5637b44d79d17f109fdb510a7c51455892aa2d216
添加到 ZIP 中: manifests\registry.ollama.ai\library\qwen\0.5b
zip文件创建成功: qwen-0.5b.zip

目标电脑

将打包的 zip 拷贝到目标电脑 models 下直接解压到当前目录即可

ollama

ollama list 输出所有模型命令

NAME                ID              SIZE      MODIFIED
gemma2:9b ff02c3702f32 5.4 GB 5 hours ago
phi:2.7b e2fd6321a5fe 1.6 GB 5 hours ago
qwen:7b 2091ee8c8d8f 4.5 GB 4 days ago
qwen:0.5b b5dc5e784f2a 394 MB 4 days ago
llama3.1:8b 46e0c10c039e 4.9 GB 5 days ago
deepseek-r1:1.5b a42b25d8c10a 1.1 GB 6 days ago
deepseek-r1:7b 0a8c26691023 4.7 GB 8 days ago

Ollama 在 Windows 上存储文件在几个不同的位置。您可以通过以下步骤查看它们:

  • <cmd>+R 键并输入:

    • explorer %LOCALAPPDATA%\Ollama :包含日志和下载的更新

      • app.log:最近的 GUI 应用程序日志
      • server.log:最近的服务器日志
      • upgrade.log:升级日志输出
    • explorer %LOCALAPPDATA%\Programs\Ollama:包含二进制文件(安装器将其添加到用户 PATH 中)

    • explorer %HOMEPATH%\.ollama:包含模型和配置

    • explorer %TEMP%:包含临时执行文件,在一个或多个 ollama* 目录中

源码

/**
* @Time : 2025/2/14
* @File : main.go
* @Software: ollamab
* @Author : Mr.Fang
* @Description: 备份 ollama 模型
*/ package main import (
"archive/zip"
"encoding/json"
"fmt"
"io"
"log"
"os"
"path/filepath"
"runtime"
"strings"
) var modelPath = "" // 初始化获取模型文件路径,优先从系统环境变量获取,其次获取默认路径
func init() {
models := os.Getenv("OLLAMA_MODELS")
if len(models) > 0 {
modelPath = models
} else {
// Window
if runtime.GOOS == "windows" {
home, err := os.UserHomeDir()
if err != nil {
log.Panicln("获取用户主目录失败:", err)
}
modelPath = filepath.Join(home, ".ollama", "models")
} else { // Linux
modelPath = filepath.Join("/usr/share/ollama/", ".ollama", "models")
}
}
fmt.Println("模型路径:", modelPath)
} // 将文件或文件夹添加到 zip 中
func addToZip(zipWriter *zip.Writer, filePath, baseFolder string) error {
// 计算文件的相对路径
relativePath, err := filepath.Rel(baseFolder, filePath)
if err != nil {
return err
} fmt.Println("添加到 ZIP 中:", relativePath) // 如果是目录,则创建空目录
info, err := os.Stat(filePath)
if err != nil {
return err
} // 如果是目录,创建空目录
if info.IsDir() {
_, err := zipWriter.Create(relativePath + "/")
return err
} // 如果是文件,创建文件条目
fileInZip, err := zipWriter.Create(relativePath)
if err != nil {
return err
} // 打开文件并复制内容到zip中
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close() // 复制文件内容到zip
_, err = io.Copy(fileInZip, file)
return err
} // 模型相关所有文件路径
func blobsPath(modelDataPath, basePath string) []string {
file, err := os.ReadFile(modelDataPath)
if err != nil {
fmt.Println("model data 文件读取错误!", err)
}
// 转 map
var modelData map[string]interface{}
var blobsPath []string
err = json.Unmarshal(file, &modelData)
if err != nil {
fmt.Println("model data 转换错误!", err)
}
// 层数据
layers := modelData["layers"].([]interface{})
// 模型详情信息
layers = append(layers, modelData["config"].(interface{})) for _, layer := range layers {
item := layer.(map[string]interface{})
digest := item["digest"].(string) // sha256
digest = strings.ReplaceAll(digest, ":", "-")
join := filepath.Join(basePath, "blobs", digest)
// 使用 os.Stat 检查文件是否存在
fileInfo, _ := os.Stat(join)
if fileInfo != nil {
blobsPath = append(blobsPath, join)
}
}
return blobsPath
} // build 打包 zip
func build(name string, output string, folderPaths []string) {
fmt.Println("开始打包,耐心等待…………")
// 创建目标zip文件
zipFilePath := filepath.Join(output, name)
zipFile, err := os.Create(zipFilePath)
if err != nil {
fmt.Println("创建zip文件失败:", err)
return
}
defer zipFile.Close() // 创建zip写入器
zipWriter := zip.NewWriter(zipFile)
defer zipWriter.Close() // 逐个添加文件或目录到zip文件中
for _, filePath := range folderPaths {
// 注意:baseFolder 是我们希望在zip文件中根目录
err := addToZip(zipWriter, filePath, modelPath)
if err != nil {
fmt.Println("添加文件到zip失败:", err)
return
}
}
fmt.Println("zip文件创建成功:", zipFilePath)
} func main() {
args := os.Args[1:] if len(args) == 0 {
fmt.Println("参数: ollamab 名称:型号(必填) 指定输出路径,默认输出当前路径(可选)")
fmt.Println("示例: ollamab deepseek-r1:1.5b ")
fmt.Println("示例: ollamab deepseek-r1:1.5b D:/models")
fmt.Println("示例: ollamab lrs33/bce-embedding-base_v1:latest")
return
}
arg := strings.Split(args[0], ":")
name := arg[0]
version := arg[1]
output := "./"
if len(args) == 2 {
output = args[1]
}
// 配置文件路径
library := filepath.Join(modelPath, "manifests", "registry.ollama.ai", "library", name, version)
// 特殊情况,用户自己分享的模型
contains := strings.Contains(name, "/")
if contains {
libs := strings.Split(name, "/")
library = filepath.Join(modelPath, "manifests", "registry.ollama.ai", libs[0], libs[1], version)
// 替换 "/" 否则无法创建 zip
name = strings.ReplaceAll(name, "/", "-")
}
folderPaths := blobsPath(library, modelPath)
// 模型路径
folderPaths = append(folderPaths, library)
// 打包
build(fmt.Sprintf("%s-%s.zip", name, version), output, folderPaths) }

更新说明

  • 解决用户自己分享的模型备份路径问题
  • 模型 config 配置文件未拷贝导致 ollama list 未显示迁移模型,需要执行 ollama run 模型
  • Linux 下默认模型路径未处理

Ollama 模型迁移备份工具 ollamab的更多相关文章

  1. 规划将 IaaS 资源从经典部署模型迁移到 Azure Resource Manager

    尽管 Azure 资源管理器提供了许多精彩功能,但请务必计划迁移,以确保一切顺利进行. 花时间进行规划可确保执行迁移活动时不会遇到问题. Note 以下指导的主要参与者为 Azure 客户顾问团队,以 ...

  2. 有关从经典部署模型迁移到 Azure Resource Manager 部署模型的常见问题

    此迁移计划是否影响 Azure 虚拟机上运行的任何现有服务或应用程序? 不可以. VM(经典)是公开上市的完全受支持的服务. 你可以继续使用这些资源来拓展你在 Azure 上的足迹. 如果我近期不打算 ...

  3. 使用 Azure PowerShell 将 IaaS 资源从经典部署模型迁移到 Azure Resource Manager

    以下步骤演示了如何使用 Azure PowerShell 命令将基础结构即服务 (IaaS) 资源从经典部署模型迁移到 Azure Resource Manager 部署模型. 也可根据需要通过 Az ...

  4. 使用 Azure CLI 将 IaaS 资源从经典部署模型迁移到 Azure Resource Manager 部署模型

    以下步骤演示如何使用 Azure 命令行接口 (CLI) 命令将基础结构即服务 (IaaS) 资源从经典部署模型迁移到 Azure Resource Manager 部署模型. 本文中的操作需要 Az ...

  5. MySQL 数据库备份种类以及常用备份工具汇总

    1,数据库备份种类 按照数据库大小备份,有四种类型,分别应用于不同场合,下面简要介绍一下: 1.1完全备份 这是大多数人常用的方式,它可以备份整个数据库,包含用户表.系统表.索引.视图和存储过程等所有 ...

  6. Mysql备份工具mysqldump和mysqlhotcopy

    (1).Mysql备份类型 1)按照备份时对数据库的影响分为 Hot backup(热备):也叫在线备份.指在数据库运行中直接备份,对正在运行的数据库没有任何影响. Cold backup(冷备):也 ...

  7. [转帖]服务器备份工具:Amanda,Bakula,Clonezilla,Rsnapshot,Mondo Rescue

    服务器备份工具:Amanda,Bakula,Clonezilla,Rsnapshot,Mondo Rescue https://ywnz.com/linuxyffq/5270.html 改天试用一下. ...

  8. MySQL数据备份之逻辑备份工具mysqldump

    #前言:我们知道对数据进行备份很重要,出现非正常操作可以进行对数据进行恢复,下面我们就来使用一下mysql数据库自带的一个逻辑备份工具mysqldump 1.简单概述 #mysqldump:mysql ...

  9. SQLSERVER自动定时(手动)备份工具

    最近项目需要,写了一个小工具软件: 1.实时显示监控 2.可多选择备份数据库 3.按每天定时备份 4.备份文件自动压缩 5.删除之前备份文件 直接上图 1.备份监控界面: 2.数据库设置: 附工具下载 ...

  10. MySQL 5.7 mysqlpump 备份工具说明

    背景: MySQL5.7之后多了一个备份工具:mysqlpump.它是mysqldump的一个衍生,mysqldump就不多说明了,现在看看mysqlpump到底有了哪些提升,可以查看官方文档,这里针 ...

随机推荐

  1. 物联网CC2530按键单双击分别控制两灯

    (1)确定思路单击和双击的效果分别是怎样的(此文章采用简单的延时函数不涉及中断).首先可以定义一个普通延时delay和一个标志位count变量,这里需有个延时阈值咱们直接可以宏定义B值(这里需要注意宏 ...

  2. Kettle连接MySQL数据库时提示Driver class 'org.gjt.mm.mysql.Driver' could not be found

    使用Kettle连接MySQL数据库时,提示以下连接提示信息导致我们无法对数据库进行连接: Driver class 'org.gjt.mm.mysql.Driver' could not be fo ...

  3. IE低版本cors跨域请求

    标签:js 坑位 最近接到一个活动需求,但是服务端接口全是跨域的,由于js同源策略,ajax请求是不允许跨域请求的,比较流行的解决方法是jsonp或者cors,但当服务端是走cors的时候,发现IE1 ...

  4. 09C++选择结构(3)

    一.求3个整数中最小值 题目:输入三个整数,表示梨的重量,输出最小的数. 方法1:经过三次两两比较,得出最小值. a<=b && a<=c min=a b<=c &a ...

  5. Vue-Router 面试题 (2023-09-13更新)

    路由导航守卫 和 Vue 实例生成周期钩子函数的执行顺序? 路由导航守卫 都是在 Vue 实例生命周期钩子函数 之前执行的 Vue-Router 有哪几种导航钩子? 1. 全局守卫 全局前置守卫:be ...

  6. Flutter问题 Flutter MissingPluginException(No implementation found for method xxx on channel xxx)

    问题如题 有时候项目跑着跑着突然控制台就报了这个错,用hot restart也没有用,问题的本质是plugin没有找到,这时候有两种方法 flutter clean,会将依赖清除,这时候再重新pub ...

  7. 基于 MediatR 和 FluentValidation 的 CQRS 验证管线

    基于 MediatR 和 FluentValidation 的 CQRS 验证管线 CQRS Validation Pipeline with MediatR and FluentValidation ...

  8. 【C#】【平时作业】习题-9-接口

    1.什么是接口 为派生类提供因该遵守的标准结构,而本身只包含成员声明,不包含成员的定义 2.接口与抽象类有什么区别 3.设计IBluetooth. public interface IBluetoot ...

  9. 解决用netty去做web服务时,post长度过大的问题

    原文地址 http://my.oschina.net/momohuang/blog/114552 先说一下,本来是想自己写socket ,启动一个简单点的web服务用于接收数据的.写完之后,发现会有各 ...

  10. Qt音视频开发6-ffmpeg解码处理

    一.前言 采用ffmpeg解码,是所有视频监控开发人员必备的技能,绕不过去的一个玩意,甚至可以说是所有音视频开发人员的必备技能.FFmpeg是一套可以用来记录.转换数字音频.视频,并能将其转化为流的开 ...