
文件附件处理是OA系统的核心功能之一,涉及文件上传、存储、下载、预览、删除等多个环节。通达OA提供了完善的文件附件处理机制,通过 utility_file.php 文件中的一系列函数实现。本文将深入解析OA的文件附件处理机制,包括上传流程、存储结构、权限控制和最佳实践。

通达OA的附件存储采用"数据库+文件系统"的架构:
数据库存储:
文件系统存储:

OA使用特殊的编码方式生成附件ID:

upload($field_name, $allow_types = "", $max_size = 0, $module = "", $uid = 0)功能:处理文件上传
参数说明:
参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
field_name | string | - | 文件表单字段名 |
allow_types | string | "" | 允许的文件类型 |
max_size | int | 0 | 最大文件大小(KB),0为不限制 |
module | string | "" | 模块名称 |
uid | int | 0 | 用户ID,默认当前用户 |
返回值:
返回字段 | 说明 |
|---|---|
error | 错误码(0=成功,其他=失败) |
id | 附件ID |
name | 原始文件名 |
size | 文件大小 |
type | 文件类型 |
path | 文件路径 |
示例:
include_once("inc/utility_file.php");
// 处理文件上传
if ($_FILES['attachment']['name']) {
$result = upload('attachment', '', 20480, 'project');
if ($result['error'] == 0) {
$attach_id = $result['id'];
// 保存附件ID到业务表
$td->query("UPDATE project SET attach_id = '$attach_id' WHERE project_id = 1");
} else {
Message(_("上传失败:") . $result['error'], "", "error");
}
}
<form method="post" action="upload.php" enctype="multipart/form-data">
<input type="file" name="attachment" multiple>
<button type="submit"><?php echo _("上传"); ?></button>
</form>注意事项:
enctype="multipart/form-data"4.1.1 add_attach() - 添加附件
add_attach($module, $record_id, $attach_ids, $uid = 0)功能:为记录添加附件
参数说明:
参数 | 类型 | 说明 |
|---|---|---|
module | string | 模块名称 |
record_id | int | 记录ID |
attach_ids | string | 附件ID(多个用逗号分隔) |
uid | int | 用户ID |
示例:
add_attach('project', 1, '123,456,789');4.1.2 add_attach_module() - 注册附件模块
add_attach_module($module_name, $module_title)功能:注册新的附件模块
参数说明:
参数 | 类型 | 说明 |
|---|---|---|
module_name | string | 模块名称(英文) |
module_title | string | 模块标题(中文) |
示例:
add_attach_module('project', _("项目管理"));4.2.1 delete_attach() - 删除附件
delete_attach($attach_id)功能:删除指定附件
参数:$attach_id - 附件ID
返回值:true或false
示例:
delete_attach('123456');4.2.2 delete_attach_old() - 删除旧版附件
delete_attach_old($attach_id)功能:删除旧版附件格式
参数:$attach_id - 附件ID
4.3.1 attach_size() - 获取附件大小
attach_size($attach_id)功能:获取附件大小
参数:$attach_id - 附件ID
返回值:文件大小(字节)
示例:
$size = attach_size('123456');
echo "文件大小:" . format_size($size);4.3.2 attach_real_path() - 获取附件真实路径
attach_real_path($attach_id)功能:获取附件在服务器上的真实路径
参数:$attach_id - 附件ID
返回值:文件绝对路径
示例:
$path = attach_real_path('123456');
echo "文件路径:$path";4.3.3 attach_url() - 获取附件访问URL
attach_url($attach_id, $inline = false)功能:获取附件的访问URL
参数说明:
参数 | 类型 | 说明 |
|---|---|---|
attach_id | string | 附件ID |
inline | bool | 是否在线预览(true=预览,false=下载) |
返回值:附件URL
示例:
// 获取下载链接
$download_url = attach_url('123456');
// 获取预览链接
$preview_url = attach_url('123456', true);4.3.4 attach_link() - 获取附件链接
attach_link($attach_id, $text = "", $inline = false)功能:生成完整的附件链接HTML
参数说明:
参数 | 类型 | 说明 |
|---|---|---|
attach_id | string | 附件ID |
text | string | 链接文字,默认文件名 |
inline | bool | 是否在线预览 |
返回值:HTML链接代码
示例:
echo attach_link('123456', _("点击下载"));4.4.1 copy_attach() - 复制附件
copy_attach($attach_id, $new_module = "", $new_record_id = 0)功能:复制附件到新位置
参数说明:
参数 | 类型 | 说明 |
|---|---|---|
attach_id | string | 附件ID |
new_module | string | 新模块名称 |
new_record_id | int | 新记录ID |
返回值:新附件ID
示例:
$new_attach_id = copy_attach('123456', 'project', 2);4.4.2 attach_sign_key() - 生成附件签名
attach_sign_key($attach_id)功能:生成附件访问签名
参数:$attach_id - 附件ID
返回值:签名字符串
用途:用于生成安全的附件访问链接
示例:
$sign = attach_sign_key('123456');
$url = "download.php?id=123456&sign=$sign";4.4.3 attach_id_encode() / attach_id_decode() - ID编解码
// 编码
$encoded_id = attach_id_encode($raw_id);
// 解码
$raw_id = attach_id_decode($encoded_id);功能:附件ID的加密和解密
参数:$raw_id / $encoded_id - 原始ID或编码后的ID
用途:防止附件ID被猜测
5.1.1 is_uploadable() - 是否可上传
is_uploadable($filename)功能:判断文件是否允许上传
参数:$filename - 文件名
返回值:true或false
5.1.2 is_text() - 是否为文本文件
is_text($filename)功能:判断文件是否为文本文件
参数:$filename - 文件名
返回值:true或false
5.1.3 is_office() - 是否为Office文件
is_office($filename)功能:判断文件是否为Office文件
参数:$filename - 文件名
返回值:true或false
支持格式:doc, docx, xls, xlsx, ppt, pptx, pdf等
5.1.4 is_image() - 是否为图片文件
is_image($filename)功能:判断文件是否为图片文件
参数:$filename - 文件名
返回值:true或false
支持格式:jpg, jpeg, png, gif, bmp等
5.1.5 is_viewable() - 是否可在线预览
is_viewable($filename)功能:判断文件是否支持在线预览
参数:$filename - 文件名
返回值:true或false
支持格式:图片、Office文档、PDF等
5.1.6 is_media() - 是否为媒体文件
is_media($filename)功能:判断文件是否为媒体文件
参数:$filename - 文件名
返回值:true或false
支持格式:mp3, mp4, avi, flv等
5.1.7 is_editable() - 是否可编辑
is_editable($filename)功能:判断文件是否支持在线编辑
参数:$filename - 文件名
返回值:true或false
5.1.8 is_wps() - 是否为WPS文件
is_wps($filename)功能:判断文件是否为WPS格式
参数:$filename - 文件名
返回值:true或false

CreateThumb($src_file, $dst_file, $max_width, $max_height)功能:创建图片缩略图
参数说明:
参数 | 类型 | 说明 |
|---|---|---|
src_file | string | 源文件路径 |
dst_file | string | 目标文件路径 |
max_width | int | 最大宽度 |
max_height | int | 最大高度 |
返回值:true或false
示例:
CreateThumb(
"attach/project/202401/image.jpg",
"attach/project/202401/image_thumb.jpg",
200,
200
);image_mimetype($filename)功能:获取图片的MIME类型
参数:$filename - 文件名
返回值:MIME类型字符串
示例:
$mime = image_mimetype("image.jpg");
echo "MIME类型:$mime"; // 输出:image/jpegReplaceImageSrc($html, $module, $record_id)功能:替换HTML内容中的图片路径为附件URL
参数说明:
参数 | 类型 | 说明 |
|---|---|---|
html | string | HTML内容 |
module | string | 模块名称 |
record_id | int | 记录ID |
返回值:处理后的HTML
示例:
$content = '<img src="image.jpg">';
$content = ReplaceImageSrc($content, 'project', 1);
// 输出:<img src="/attach/project/202401/image.jpg">7.1.1 dir_size() - 计算目录大小
dir_size($dir)功能:计算目录占用空间大小
参数:$dir - 目录路径
返回值:目录大小(字节)
示例:
$size = dir_size("attach/project");
echo "目录大小:" . format_size($size);7.1.2 delete_dir() - 删除目录
delete_dir($dir)功能:删除目录及其所有内容
参数:$dir - 目录路径
返回值:true或false
7.1.3 dir_file_nums() - 统计目录文件数
dir_file_nums($dir)功能:统计目录中的文件数量
参数:$dir - 目录路径
返回值:文件数量
7.2.1 backup_file() - 备份文件
backup_file($src_file, $dst_file)功能:备份文件
参数说明:
参数 | 类型 | 说明 |
|---|---|---|
src_file | string | 源文件路径 |
dst_file | string | 目标文件路径 |
返回值:true或false
7.2.2 td_copy() - 文件复制
td_copy($src_file, $dst_file)功能:复制文件
参数说明:
参数 | 类型 | 说明 |
|---|---|---|
src_file | string | 源文件路径 |
dst_file | string | 目标文件路径 |
返回值:true或false
7.2.3 td_rename() - 文件重命名
td_rename($old_name, $new_name)功能:重命名文件
参数说明:
参数 | 类型 | 说明 |
|---|---|---|
old_name | string | 旧文件名 |
new_name | string | 新文件名 |
返回值:true或false
7.2.4 td_file_put_contents() - 写入文件
td_file_put_contents($file, $content)功能:写入文件内容
参数说明:
参数 | 类型 | 说明 |
|---|---|---|
file | string | 文件路径 |
content | string | 文件内容 |
返回值:写入的字节数或false
<?php
include_once("inc/auth.inc.php");
include_once("inc/header.inc.php");
include_once("inc/utility_file.php");
if ($_POST) {
$attach_ids = array();
// 处理文件上传
if ($_FILES['attachments']['name'][0]) {
foreach ($_FILES['attachments']['name'] as $key => $name) {
// 临时修改$_FILES结构
$temp_file = array(
'name' => $_FILES['attachments']['name'][$key],
'type' => $_FILES['attachments']['type'][$key],
'tmp_name' => $_FILES['attachments']['tmp_name'][$key],
'error' => $_FILES['attachments']['error'][$key],
'size' => $_FILES['attachments']['size'][$key]
);
$_FILES['attachment'] = $temp_file;
$result = upload('attachment', '', 20480, 'project');
if ($result['error'] == 0) {
$attach_ids[] = $result['id'];
}
}
}
// 保存到数据库
if (!empty($attach_ids)) {
$attach_id_str = implode(',', $attach_ids);
$td->query("INSERT INTO project_attach (project_id, attach_ids) VALUES (1, '$attach_id_str')");
}
Message(_("上传成功"), "index.php", "success");
exit;
}
?>
<form method="post" enctype="multipart/form-data">
<input type="file" name="attachments" multiple>
<button type="submit"><?php echo _("上传附件"); ?></button>
</form>
<?php
include_once("inc/footer.inc.php");
?><?php
include_once("inc/auth.inc.php");
include_once("inc/header.inc.php");
include_once("inc/utility_file.php");
// 获取附件列表
$result = $td->query("SELECT attach_ids FROM project WHERE project_id = 1");
$row = $td->fetch_array($result);
$attach_ids = explode(',', $row['attach_ids']);
?>
<div class="attach-list">
<h3><?php echo _("附件列表"); ?></h3>
<?php foreach ($attach_ids as $attach_id) { ?>
<div class="attach-item">
<?php echo attach_link($attach_id); ?>
<span class="size">(<?php echo format_size(attach_size($attach_id)); ?>)</span>
<a href="delete_attach.php?id=<?php echo $attach_id; ?>"
onclick="return confirm('<?php echo _('确定删除?'); ?>')">
<?php echo _("删除"); ?>
</a>
</div>
<?php } ?>
</div>
<?php
include_once("inc/footer.inc.php");
?><?php
include_once("inc/auth.inc.php");
// 检查附件权限
function check_attach_permission($attach_id, $uid) {
global $td;
// 查询附件所属记录
$sql = "SELECT * FROM project_attach WHERE attach_ids LIKE '%$attach_id%'";
$result = $td->query($sql);
if ($td->num_rows($result) > 0) {
$row = $td->fetch_array($result);
// 检查项目权限
$project_result = $td->query("SELECT creator_uid FROM project WHERE project_id = " . $row['project_id']);
$project_row = $td->fetch_array($project_result);
if ($project_row['creator_uid'] == $uid || is_module_manager($uid, 'project')) {
return true;
}
}
return false;
}
// 使用示例
$attach_id = $_GET['id'];
if (!check_attach_permission($attach_id, $_SESSION['uid'])) {
Message(_("无权限访问该附件"), "", "error");
exit;
}
// 允许访问附件
$path = attach_real_path($attach_id);
// 输出文件内容
?>
安全建议:
安全项 | 建议 |
|---|---|
文件类型 | 使用白名单机制,只允许上传已知安全的文件类型 |
文件大小 | 设置合理的大小限制,防止DoS攻击 |
文件名 | 过滤特殊字符,避免路径遍历攻击 |
权限控制 | 验证用户是否有权限访问附件 |
文件归属 | 检查附件是否属于用户有权访问的记录 |

优化建议:
优化项 | 建议 |
|---|---|
存储结构 | 按模块和日期分目录存储,便于管理和清理 |
缓存策略 | 缓存附件元数据,减少数据库查询 |
CDN加速 | 使用CDN加速附件下载 |
文件压缩 | 对图片和文档进行压缩处理 |
清理策略 | 定期清理过期或无用的附件 |

备份建议:
备份项 | 说明 |
|---|---|
数据库 | 备份附件表和关联业务表 |
文件系统 | 备份MYOA\attach目录 |
增量备份 | 只备份新增和修改的文件 |
恢复测试 | 定期测试备份文件的可用性 |
现象:文件上传失败,返回错误码
解决方案:

错误码说明:
错误码 | 原因 | 解决方案 |
|---|---|---|
1 | 文件大小超过PHP配置限制 | 修改php.ini中的upload_max_filesize |
2 | 文件类型不在白名单 | 修改allow_types参数 |
3 | 服务器磁盘空间不足 | 清理磁盘空间 |
4 | 临时目录无写入权限 | 检查临时目录权限 |
现象:点击附件链接无法下载
解决方案:
问题类型 | 排查方法 |
|---|---|
附件ID错误 | 检查attach_id是否正确 |
文件已删除 | 检查文件是否存在于attach目录 |
权限不足 | 检查用户权限 |
路径错误 | 使用attach_real_path()验证路径 |
现象:Office文件无法在线预览
解决方案:
问题类型 | 排查方法 |
|---|---|
OC服务未启动 | 检查Office_OC服务状态 |
文件格式不支持 | 检查是否为支持的格式 |
文件损坏 | 验证文件完整性 |
内存不足 | 增加MemoryMax配置 |
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。