Excel2007打不开xlsx文件,这在我的日常工作中是非常常见的现象,究其原因是因为大家用的Excel版本五花八门,而恰好不同版本的Excel的xml代码规范又不一样,于是一个用Excel2013做的表格,如果里面还有很多批注或者数据验证等等内容......那我几乎可以预见接下来就是开始找做表格的人要一份xls格式的表格。因为在不装更新兼容包的情况下,上面说的那种情况,Excel基本上没法打开文件,或者打开了文件,批注被全部修复(直接删除)。
在很早前我就想过这个问题,为什么同样是xlsx文件,Excel2007会认为高版本Excel的批注会有问题?后来在Excelhome我找到了答案......原来仅仅是因为高版本Excel的comments文件里多了一串代码:ShapeId="0"。如果要手动修复,就把xlsx改成rar或者zip,然后把xl/comments*.xml文件都解压出来,把shapeId="0"替换成空值,把文件再放回压缩包,改回xlsx,再次打开文件,Excel2013做的表格这个时候就应该能正常打开了,至于Excel2016的表格,会还需要修复一次,因为批注的代码格式又变了。
于是愚钝的我就为这个想法付出了代价(真实原因是我实在是太菜了,连基础都没学好就开始跑)
using System;
using System.Text;
using System.Windows.Forms;
using System.IO.Compression;
using System.IO;
using CZip = ICSharpCode.SharpZipLib.Zip;
private void Button1_Click(object sender, EventArgs e)
{
int i = 0;
OpenFileDialog path = new OpenFileDialog();
//文件类型过滤
path.Filter = @"待修复Excel表格|*.xlsx";
//展开文件选择对话框
DialogResult result = path.ShowDialog();
if (result == DialogResult.OK)
{
//获取导出文件夹目录
string expath = path.FileName + @"_Fix";
if(Directory.Exists(expath))
{
DialogResult fg= MessageBox.Show("导出数据已存在!是否覆盖?覆盖后将失去所有已导出数据!", "警告", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (fg == DialogResult.Yes)//确认删除
{
//对重复文件夹进行删除操作
DirectoryInfo dl = new DirectoryInfo(expath);
dl.Delete(true);
DialogResult qr= MessageBox.Show("删除命令已执行,是否继续?", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (qr == DialogResult.Yes)
{
//继续执行命令
}
else
{
if (qr == DialogResult.No)//删除文件夹后不继续
{
i = 1;
}
}
}
else
{
if (fg == DialogResult.No)//确认不删除
{
i = 1;
}
}
}
//强制用户确认后才进行解压操作
if (i == 0)
{
//获取打开文件的路径(全路径,含文件名)
int fnlength = path.ToString().LastIndexOf("FileName");
string sFile = path.ToString().Substring(fnlength + 9, path.ToString().Length - fnlength - 9);
//获取打开文件所在目录,传递至sourcepath
string sourcepath = Path.GetDirectoryName(sFile);
//开始解压文件
ZipFile.ExtractToDirectory(path.FileName, expath);
//跳转至xl文件夹下
string copath = expath + "\\xl\\";
//获取xl文件夹下所有带有comments字符的xml文件路径
string[] pathFile = Directory.GetFiles(copath, "comments*.xml", SearchOption.TopDirectoryOnly);
//将中间文件命名为cobak.xml
string strcon = copath + @"cobak.xml";
//暂时将替换容器内容清空
string con = "";
foreach (string str in pathFile)
{
StreamReader reader = new StreamReader(str, Encoding.UTF8);
//读取至文件尾
con = reader.ReadToEnd();
//替换shapeId="0"为空值
con = con.Replace(@"shapeId=""0""", "");
//替换Excel2013的不正确的XML代码块
con = con.Replace(@"</commment></commentList></comments>", "");
//开始以UTF-8编码开始写操作
StreamWriter writer = new StreamWriter(strcon, false, Encoding.UTF8);
//写入已替换的内容
writer.Write(con);
//释放写入缓冲区,暂时关闭写入器
writer.Flush();
writer.Close();
reader.Close();
//将写入文件复制为原文件,删除写入文件
File.Copy(strcon, str, true);
File.Delete(strcon);
}
//到此替换写入操作已完成
//开始修改文件扩展名
string nFile = Path.ChangeExtension(sFile, ".zip");
FileInfo fi = new FileInfo(sFile);
fi.MoveTo(nFile);
//开始Czip操作
CZip.ZipFile zip = new CZip.ZipFile(nFile);
zip.BeginUpdate();
//获取每个comments.xml文件的目录
foreach (string str in pathFile)
{
string filename = Path.GetFileName(str);
zip.Add(str,@"/xl/" + filename);
}
zip.CommitUpdate();
//关闭Czip
zip.Close();
//将文件扩展名改回xlsx
string endFile =Path.ChangeExtension(nFile, ".xlsx");
//删除临时目录
fi.CopyTo(endFile, true);
DirectoryInfo dl = new DirectoryInfo(expath);
dl.Delete(true);
//删除临时文件
File.Delete(nFile);
System.Diagnostics.Process.Start("explorer.exe", sourcepath);
}
}
现在回头看自己写的代码......真是蠢爆了,一堆if判断先不说,同时用System.IO.Compression.ZipFile和ICSharpCode.SharpZipLib.Zip,自己也许根本就没想明白,只想着程序怎么顺利执行就完事,在调试的时候发现线程冲突也只是用另一串代码解决。
不过中间有一点没有想到的是sharpzip要将指定文件压缩到指定目录的用法倒是比我想得简单,原本以为会要用zipEntry,结果根本不需要.......只要指定好路径就OK了,过程完美模拟手动放文件进压缩包。
反正这个程序用处太小,我就当自娱自乐了。