4
头图

Windows 版微信升级到 3.9 之后,接收到的文件都变成了只读属性,对需要经常修改微信接收文件进行交流的人来说极为不便。虽然从业务功能上来说,需要频繁交流的文档还是用在线协同(比如腾讯文档)比较好一些,但从技术的角度来看,应该如何解决这个问题呢?

其实很多技术栈都提供了监听系统文件变化的 API,比如 .NET 就在其 System.IO 命名空间下提供了 FileSystemWatcher 类用于监听文件的变化,包括创建文件、删除文件、文件改名/移动、文件属性变化等。所以可以使用 C# 和 .NET 快速的写一个程序用来监听指定文件夹下的新建文件,如果新创建的文件属性是只读,则去掉其只读属性。

第一步,找到微信接收文件的目录。

在微信的「设置→文件管理」中就可以找到,比如 C:\Users\James\Documents\WeChat Files。然后需要识别这个目录下的用户目录,一般是微信号。点击微信窗口上自己的头像就可以看到。用户目录下的 FileStorage\File 目录就是我们的目标目录了。

为了帮助用户快速定位到这个目录,在假设用户没有手工改变微信文件目录的情况下,可以这样来查找

  • 通过 Environment.GetFolderPath() 获取到当前用户的“文档”目录;
  • 拼接得到 WeChat Files 目录的路径;
  • 遍历 WeChat Files 目录下的子目录,去掉特殊命名的 All UsersApplet 等,在剩下的子目录中进一步猜测
  • 一般最近访问过的就是自己的微信账号目录

那么整个猜测目录的过程代码示例如下:

string? GuessReceivePath() {
    var docDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
    var wxDir = Path.Combine(docDir, "WeChat Files");
    if (!Directory.Exists(wxDir)) { return null; }

    HashSet<string> excludes = new(StringComparer.InvariantCulture) {
        "All Users",
        "Applet"
    };

    var userDir = Directory.EnumerateDirectories(wxDir)
        .Select(it => new FileInfo(it))         // 对每个目录产生 FileInfo 对象(小性能损耗可以忽略)
        .ExceptBy(excludes, fi => fi.Name)      // 去掉特定名称的目录
        .OrderByDescending(it => it.LastAccessTime)    // 按最近访问时间排序
        .FirstOrDefault()                       // 取最最后访问的那一个,注意有可能是 null
        ?.FullName;                             // 取得完整路径

    if (userDir == null) { return null; }
    return Path.Combine(userDir, "FileStorage", "File");
}

当然,猜测的目录不一定准确,所以写程序的时候最好能提供给用户一个可以手工修改/设置目录的手段,当然别忘了目录的有效性检查(检查目录存在)。

第二步,监听文件创建

监听文件变化只需要新建一个 FileSystemWatcher 对象,设置一些属性即可。

  • 设置 Path 到要监听的目录
  • 设置 .IncludeSubdirectories = true 表示需要监听其子目录
  • 添加 Created 事件在监听到有文件创建时进行处理

示例代码:

var watcher = new FileSystemWatcher() {
    IncludeSubdirectories = true,
};

watcher.Created += (_, e) => { ... };

第三步,对监听到的新建文件进行处理

如果目录下有新创建的文件,上面 Created 事件的 e 参数(FileSystemEventArgs 类型)会携带文件的信息,通过 e.FullPath 可以拿文件的完整路径。然后使用 FileInfo 判断其是否只读,并解除其只读属性即可:

(_, e) => {
    var info = new FileInfo(e.FullPath);
    if (info.IsReadOnly) {
        info.IsReadOnly = false;
        Log($"[去掉只读] {e.Name}");
    }
};

第四步,监听文件属性变更

实际使用中发现,只有在发送文件的时候,会在微信文件目录创建只读文件。接收文件的时候多数是收到的非只读文件,然后再改成只读属性的。所以还需要监听文件属性的变化。

这样就需要设置 FileSystemWatcher 对象的 NotifyFilter 属性,把 NotifyFilters.Attributes 标记加上。NotifyFilter 的默认值是 LastWriteFileNameDirectoryName,所以修改之后(不需要处理目录)是:

var watcher = new FileSystemWatcher() {
    IncludeSubdirectories = true,
    NotifyFilter = NotifyFilters.LastWrite
        | NotifyFilters.Attributes
        | NotifyFilters.FileName,
};

此外还需要为 wathcer 添加 Changed 事件的处理函数,在这里检查并修改文件属性:

watcher.Changed += (_, e) => {
    new FileInfo(e.FullPath).Also(info => {
        if (info.IsReadOnly) {
            info.IsReadOnly = false;
            Log($"[改为可写] {e.Name}");
        }
    });
};
注:AlsoViyi.Util 提供的扩展。

第五步,界面和用户体验

麻雀虽小,但也应该是五脏俱全。核心功能完成之后就需要考虑用户操作界面和使用体验上的一些问题。不用考虑得太复杂,但至少也应该考虑:

  • 这是一个常驻类应用,虽然不用注册成系统服务,但也应该考虑使用系统任务栏图标方式常驻;
  • 允许用户修改路径,开始/停止监听操作;
  • 提供简单的日志功能,让用户知道它处理了哪些文件。

image.png


相关资源


边城
59.8k 声望29.6k 粉丝

一路从后端走来,终于走在了前端!