flutter開發實戰-log日志存儲zip上傳,發送釘釘機器人消息

flutter開發實戰-log日志存儲zip上傳,發送釘釘機器人消息

當我們需要Apk上傳的時候,我們需要將日志打包并上傳到七牛,上傳之后通過釘釘通知我們日志下載地址。
這里我使用的是loggy來處理日志

一、引入loggy日志格式插件

在工程的pubspec.yaml中引入插件

  loggy: ^2.0.2

使用loggy,當引入loggy后,可以在main方法中進行初始化

import 'package:loggy/loggy.dart';main() {Loggy.initLoggy();
}

之后在需要的地方進行輸入相關日志

import 'package:loggy/loggy.dart';class DoSomeWork {DoSomeWork() {logDebug('This is debug message');logInfo('This is info message');logWarning('This is warning message');logError('This is error message');}
}

二、日志存儲到本地

查看loggy源碼看到,日志沒有存儲到本地,在日志的目錄下可以看到printers目錄,LoggyPrinter為printer的抽象類
在這里插入圖片描述

part of loggy;/// Printer used to show logs, this can be easily swapped or replaced
abstract class LoggyPrinter {const LoggyPrinter();void onLog(LogRecord record);
}

當然我們需要通過繼承該類來實現將日志內容寫入到本地文件中,這時候我們定義了一個FilePrinter,實現onLog方法將日志寫入文件中。

  • 創建日志File

我們需要指定log日志所在目錄,可以使用path_provider來獲取document、tmp等目錄。

Future<String> createDirectory(String appTAG) async {final Directory directory = await getApplicationDocumentsDirectory();var file = Directory("${directory.path}/$appTAG");try {bool exist = await file.exists();if (exist == false) {await file.create();}} catch (e) {print("createDirectory error");}return file.path;
}

創建日志File

Future<void> getDirectoryForLogRecord() async {String currentDay = getCurrentDay();if (currentDay != _currentDate) {final String fileDir = await createDirectory(this.appTAG);file = File('$fileDir/$currentDay.log');_sink = file!.openWrite(mode: overrideExisting ? FileMode.writeOnly : FileMode.writeOnlyAppend,encoding: encoding,);_currentDate = currentDay;}}
  • 處理日志跨天的情況

每一天的日志對應一個date.log文件,如20240101.log
如果出現正好跨天的情況下,需要生成新的File來處理。這里定義了一個定時器,10分鐘檢測一次,如果日期不一致,則重新創建File

// 定時器void startTimer() {timerDispose();_timer = Timer.periodic(dateCheckDuration!, (timer) {getDirectoryForLogRecord();});}void timerDispose() {_timer?.cancel();_timer = null;}
  • IOSink寫入文件

寫入文件,我們需要用到IOSink,

寫入的文件file調用openWrite即可獲得IOSink對象。
openWrite方法如下

IOSink openWrite({FileMode mode: FileMode.write, Encoding encoding: utf8});

默認情況下寫入是會覆蓋整個文件的,但是可以通過下面的方式來更改寫入模式

IOSink ioSink = logFile.openWrite(mode: FileMode.append);

IOSink寫入文件流程如下

var logFile = File('log.txt');
var sink = logFile.openWrite();
sink.write('FILE ACCESSED ${DateTime.now()}\n');
await sink.flush();
await sink.close();

通過onLog方法輸入的record

@overridevoid onLog(LogRecord record) async {_sink?.writeln('${record.time} [${record.level.toString().substring(0, 1)}] ${record.loggerName}: ${record.message}');}

通過sink將文件保存到文件中。

完整的FilePrinter如下

import 'dart:async';
import 'dart:convert';
import 'dart:io';import 'package:loggy/loggy.dart';
import 'package:path_provider/path_provider.dart';
import 'package:common_utils/common_utils.dart';Future<String?> getDirectory(String appTAG) async {final Directory directory = await getApplicationDocumentsDirectory();var file = Directory("${directory.path}/$appTAG");try {bool exist = await file.exists();if (exist == true) {return file.path;}} catch (e) {print("createDirectory error");}return null;
}Future<String> createDirectory(String appTAG) async {final Directory directory = await getApplicationDocumentsDirectory();var file = Directory("${directory.path}/$appTAG");try {bool exist = await file.exists();if (exist == false) {await file.create();}} catch (e) {print("createDirectory error");}return file.path;
}// 輸出的文本文件, 開啟定時器處理跨天的log存儲問題
class FilePrinter extends LoggyPrinter {final bool overrideExisting;final Encoding encoding;final String appTAG;// 檢查日期時長,可能出現跨天的情況,比如十分鐘檢測一次,Duration? dateCheckDuration;IOSink? _sink;File? file;String? _currentDate;// 定時器Timer? _timer;FilePrinter(this.appTAG, {this.overrideExisting = false,this.encoding = utf8,this.dateCheckDuration = const Duration(minutes: 10),}) {directoryLogRecord(onCallback: () {// 開啟定時器startTimer();});}void directoryLogRecord({required Function onCallback}) {getDirectoryForLogRecord().whenComplete(() {onCallback();});}Future<void> getDirectoryForLogRecord() async {String currentDay = getCurrentDay();if (currentDay != _currentDate) {final String fileDir = await createDirectory(this.appTAG);file = File('$fileDir/$currentDay.log');_sink = file!.openWrite(mode: overrideExisting ? FileMode.writeOnly : FileMode.writeOnlyAppend,encoding: encoding,);_currentDate = currentDay;}}String getCurrentDay() {String currentDate =DateUtil.formatDate(DateTime.now(), format: "yyyyMMdd");return currentDate;}// 文件刪除后重新設置logFuture<void> resetLogFile() async {_currentDate = null;getDirectoryForLogRecord();}@overridevoid onLog(LogRecord record) async {_sink?.writeln('${record.time} [${record.level.toString().substring(0, 1)}] ${record.loggerName}: ${record.message}');}dispose() {timerDispose();}// 定時器void startTimer() {timerDispose();_timer = Timer.periodic(dateCheckDuration!, (timer) {getDirectoryForLogRecord();});}void timerDispose() {_timer?.cancel();_timer = null;}
}// 輸出到ConsolePrinter
class ConsolePrinter extends LoggyPrinter {const ConsolePrinter() : super();@overridevoid onLog(LogRecord record) {print('${record.time} [${record.level.toString().substring(0, 1)}] ${record.loggerName}: ${record.message}');}
}// 多種同時使用的printer
class MultiPrinter extends LoggyPrinter {MultiPrinter({required this.consolePrinter,required this.filePrinter,});final LoggyPrinter consolePrinter;final LoggyPrinter filePrinter;@overridevoid onLog(LogRecord record) {consolePrinter.onLog(record);filePrinter.onLog(record);}
}

三、日志log壓縮成zip

將日志log壓縮成zip,打包成zip時候,我們需要用到archive插件

在工程的pubspec.yaml中引入插件

  archive: ^3.3.7

archive是一個Dart庫,用于對各種存檔和壓縮格式進行編碼和解碼。

該archive使用示例如下

import 'dart:io';
import 'package:archive/archive_io.dart';Future<void> main() async {// Read the Zip file from disk.final bytes = File('test.zip').readAsBytesSync();// Decode the Zip filefinal archive = ZipDecoder().decodeBytes(bytes);// Extract the contents of the Zip archive to disk.for (final file in archive) {final filename = file.name;if (file.isFile) {final data = file.content as List<int>;File('out/$filename')..createSync(recursive: true)..writeAsBytesSync(data);} else {Directory('out/$filename').createSync(recursive: true);}}// Encode the archive as a BZip2 compressed Tar file.final tarData = TarEncoder().encode(archive);final tarBz2 = BZip2Encoder().encode(tarData);// Write the compressed tar file to disk.final fp = File('test.tbz');fp.writeAsBytesSync(tarBz2);// Zip a directory to out.zip using the zipDirectory convenience methodvar encoder = ZipFileEncoder();await encoder.zipDirectoryAsync(Directory('out'), filename: 'out.zip');// Manually create a zip of a directory and individual files.encoder.create('out2.zip');await encoder.addDirectory(Directory('out'));await encoder.addFile(File('test.zip'));encoder.closeSync();
}

壓縮log,我這里創建一個log_archive類

  • 首先創建zip目錄
Future<String?> getZipDir() async {final Directory directory = await getApplicationDocumentsDirectory();var file = Directory("${directory.path}/zip");try {bool exist = await file.exists();if (exist == false) {await file.create();}} catch (e) {print("createDirectory error");}return file.path;
}Future<void> setZipPath({String? zipName}) async {if (!(zipName != null && zipName.isNotEmpty)) {String currentTime =DateUtil.formatDate(DateTime.now(), format: "yyyy_MM_dd_HH_mm_ss");zipName = "$currentTime.zip";}if (!zipName.endsWith(".zip")) {zipName = "$zipName.zip";}String? zipDir = await getZipDir();if (zipDir != null && zipDir.isNotEmpty) {String zipPath = "${zipDir}/${zipName}";this.zipPath = zipPath;}}
  • 創建zip文件

創建zip文件,需要用到ZipFileEncoder,如果有同名的zip文件,則刪除后重新生成新的zip文件

Future<void> createZip() async {if (!(zipPath != null && zipPath!.isNotEmpty)) {return;}bool fileExists = await checkFileExists();if (fileExists == true) {// 文件存在// 刪除后重新創建File file = File(zipPath!);await file.delete();}// zip文件重新生成zipFileEncoder.open(zipPath!);}
  • 添加File文件

zipFileEncoder生成zip后,添加File問價

Future<void> addFile(File file) async {bool fileExists = await checkFileExists();if (fileExists) {await zipFileEncoder.addFile(file);await close();}}
  • 最后調用close

zipFileEncoder添加File后,需要結束編碼并關閉

Future<void> close() async {zipFileEncoder.close();}

log_archive完整代碼如下

import 'dart:convert';
import 'dart:io';
import 'package:archive/archive.dart';
import 'package:archive/archive_io.dart';
import 'package:common_utils/common_utils.dart';
import 'package:path_provider/path_provider.dart';Future<String?> getZipDir() async {final Directory directory = await getApplicationDocumentsDirectory();var file = Directory("${directory.path}/zip");try {bool exist = await file.exists();if (exist == false) {await file.create();}} catch (e) {print("createDirectory error");}return file.path;
}// archive
class LogArchive {String? zipPath;late ZipFileEncoder zipFileEncoder;LogArchive() {zipFileEncoder = ZipFileEncoder();}Future<void> setZipPath({String? zipName}) async {if (!(zipName != null && zipName.isNotEmpty)) {String currentTime =DateUtil.formatDate(DateTime.now(), format: "yyyy_MM_dd_HH_mm_ss");zipName = "$currentTime.zip";}if (!zipName.endsWith(".zip")) {zipName = "$zipName.zip";}String? zipDir = await getZipDir();if (zipDir != null && zipDir.isNotEmpty) {String zipPath = "${zipDir}/${zipName}";this.zipPath = zipPath;}}Future<void> createZip() async {if (!(zipPath != null && zipPath!.isNotEmpty)) {return;}bool fileExists = await checkFileExists();if (fileExists == true) {// 文件存在// 刪除后重新創建File file = File(zipPath!);await file.delete();}// zip文件重新生成zipFileEncoder.open(zipPath!);}Future<void> addFile(File file) async {bool fileExists = await checkFileExists();if (fileExists) {await zipFileEncoder.addFile(file);await close();}}Future<void> addFiles(List<File> files) async {bool fileExists = await checkFileExists();if (fileExists) {for (File file in files) {await zipFileEncoder.addFile(file);}await close();}}Future<void> close() async {zipFileEncoder.close();}Future<bool> checkFileExists() async {if (!(zipPath != null && zipPath!.isNotEmpty)) {return false;}try {File file = File(zipPath!);bool exist = await file.exists();if (exist == true) {return true;}} catch (e) {print("checkFileExists error");}return false;}// 刪除單個zip文件Future<void> zipDelete(String aZipPath) async {if (aZipPath.isEmpty) {return;}final File file = File(aZipPath);bool exist = await file.exists();if (exist == false) {print("LogArchive 文件不存在");return;}await file.delete();}// 清空zip目錄Future<void> zipClean() async {String? zipDir = await getZipDir();if (zipDir != null && zipDir.isNotEmpty) {var dir = Directory(zipDir);await dir.delete(recursive: true);}}Future<void> readZip(String zipDir) async {if (!(zipPath != null && zipPath!.isNotEmpty)) {return;}// Read the Zip file from disk.final File file = File(zipPath!);bool exist = await file.exists();if (exist == false) {print("LogArchive 文件不存在");return;}try {// InputFileStream only uses a cache buffer memory (4k by default), not the entire filevar stream = InputFileStream(zipPath!);// The archive will have the memory of the compressed archive. ArchiveFile's are decompressed on// demandvar zip = ZipDecoder().decodeBuffer(stream);for (var file in zip.files) {final filename = file.name;if (file.isFile) {final data = file.content as List<int>;final logFile = await File('${zipDir}/out/$filename')..create(recursive: true)..writeAsBytesSync(data);String logContent = await logFile.readAsString(encoding: utf8);print("readZip logContent:${logContent}");} else {await Directory('${zipDir}/out/' + filename).create(recursive: true);}// file.clear() will release the file's compressed memoryfile.clear();}} catch(e) {print("readZip e:${e.toString()}");}}
}

四、上傳到七牛

文件上傳的到七牛,需要用到七牛的qiniu_flutter_sdk插件

在工程的pubspec.yaml中引入插件

  qiniu_flutter_sdk: ^0.5.0

通過使用該插件上傳示例

    // 創建 storage 對象storage = Storage();// 創建 Controller 對象putController = PutController();// 使用 storage 的 putFile 對象進行文件上傳storage.putFile(File('./file.txt'), 'TOKEN', PutOptions(controller: putController,))
  • 七牛token獲取

我這邊使用定義個log_uploader類,由于七牛上傳需要token,token需要從服務端獲取,所以定義個一個抽象類LogTokenFetch
可以實現一個類來實現getToken。

  // 定義獲取token接口
abstract class LogTokenFetch {Future<String> getToken();
}// 下面是示例, 請勿使用
class LogTokenFetchImpl implements LogTokenFetch {@overrideFuture<String> getToken() {// TODO: implement getTokenreturn Future.value('');}
}
  • 文件上傳到七牛

我這邊使用定義個log_uploader類,本質上還是套用qiniu_flutter_sdk插件的實現。

import 'dart:convert';
import 'dart:io';import 'package:qiniu_flutter_sdk/qiniu_flutter_sdk.dart';
import 'package:crypto/crypto.dart';import 'log_token_fetch.dart';// logUploader
class LogUploader {// 創建 storage 對象final Storage storage = Storage();final PutController putController = PutController();LogTokenFetch? tokenFetch;LogUploader(this.tokenFetch) {init();}init() {// 添加狀態監聽putController.addStatusListener((StorageStatus status) {print('LogUploader status:$status');});}Future<String?> uploadFile(File zipFile, {String? customKey}) async {if (tokenFetch != null) {String? token = await tokenFetch!.getToken();if (token != null && token.isNotEmpty) {print("token:${token}");String key = customKey??md5.convert(utf8.encode(zipFile.path)).toString();PutResponse putResponse = await storage.putFile(zipFile, token, options: PutOptions(key: key,controller: putController,));return putResponse.key;} else {return null;}} else {return null;}}
}

上傳七牛過程中,可能會出現一下錯誤
StorageError [StorageErrorType.RESPONSE, 403]: {error: limited mimeType: this file type (application/octet-stream) is forbidden to upload}

當然需要確認token是否支持了對應的mimeType類型。

五、發送消息到釘釘機器人

經常會遇到釘釘的機器人消息,我們也可以調用其api實現發送消息。
請查考網址:https://open.dingtalk.com/document/orgapp/custom-robots-send-group-messages

這里使用的是插件dingtalk_bot_sender插件,當然dingtalk_bot_sender源碼也是http請求實現了發送消息到釘釘機器人的API接口。

在工程的pubspec.yaml中引入插件

  dingtalk_bot_sender: ^1.2.0

發送消息,使用的是DingTalkSender,我們可以發送markdown、url、文本等等

使用示例如下

final sender = DingTalkSender(hookUrl: hookUrl,keyword: keyword,appsecret: appsecret,);await sender.sendText('1');final markdown = '''
markdown內容''';await sender.sendMarkdown(markdown);

我這邊發送的是日志鏈接地址

final sender = DingTalkSender(hookUrl: hookUrl,keyword: keyword,appsecret: appsecret,);await sender.sendLink(title: "app日志", text: "下載地址:${url}", messageUrl: url);

到此,flutter開發實戰-log日志存儲zip上傳,發送釘釘機器人消息完成。
在這里插入圖片描述

六、小結

flutter開發實戰-log日志存儲zip上傳,發送釘釘機器人消息。

學習記錄,每天不停進步。

本文地址:https://brucegwo.blog.csdn.net/article/details/138672565

兩款小游戲

戰機長空小繩套牛
在這里插入圖片描述在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/12194.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/12194.shtml
英文地址,請注明出處:http://en.pswp.cn/web/12194.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【經驗總結】超算互聯網服務器 transformers 加載本地模型

1. 背景 使用 超算互聯網 的云服務&#xff0c;不能連接外網&#xff0c;只能把模型下載到本地&#xff0c;再上傳上去到云服務。 2. 模型下載 在 模型中 https://huggingface.co/models 找到所需的模型后 點擊下載 config.json pytorch_model.bin vocab.txt 3. 上傳模型文…

Flutter 中的 CupertinoAlertDialog 小部件:全面指南

Flutter 中的 CupertinoAlertDialog 小部件&#xff1a;全面指南 在Flutter中&#xff0c;CupertinoAlertDialog是用于在iOS風格的應用中顯示警告或提示信息的模態對話框。它以其圓角卡片和模糊背景為特點&#xff0c;為用戶提供了一個簡潔而直觀的交互界面。CupertinoAlertDi…

IT行業找工作十面十敗,不妨試試鴻蒙開發崗~

近期某脈上看到這樣一則帖子&#xff0c;討論的非常激烈&#xff01; 相信也有不少人有和他這情況類似&#xff0c;像他這種失業的狀態&#xff0c;近兩年大家或多或少都深有體驗。由于互聯網行業進過了十幾年的快速發展&#xff0c;從2G→3G→4G→5G&#xff0c;在這個期間人們…

c++ 獲取機器碼

看到網上代碼代碼都沒什么好的&#xff0c;自己備用一個 #include <iostream> #include <string> #include <sstream> #include <iomanip> #include <Windows.h> #include <iphlpapi.h> // 包含這個頭文件以獲取 PIP_ADAPTER_INFO #inclu…

elasticsearch-head 源碼運行

1、下載安裝nodejs 地址&#xff1a;Node.js — Run JavaScript Everywhere 2、git下載 elasticsearch-head 源碼 地址&#xff1a;GitHub - mobz/elasticsearch-head: A web front end for an elastic search cluster 3、使用cmd 進入 elasticsearch-head 目錄 4、依次執…

嵌入式學習-M4的基本定時器

基本介紹 框圖分析 時鐘選擇 計數器結構 開啟重裝載值寄存器的影子寄存器的工作時序圖 未開啟重裝載值寄存器的影子寄存器的工作時序圖 更新事件以及中斷 相關寄存器 相關庫函數

Cesium+山海鯨:可視化技術的完美融合

在當今數字化浪潮中&#xff0c;可視化技術已經成為各個行業提升效率和優化決策的關鍵。特別是在地理信息系統&#xff08;GIS&#xff09;和數字孿生領域&#xff0c;這種技術的重要性更加凸顯。而山海鯨可視化與Cesium的融合&#xff0c;無疑是這一領域的重大突破。 首先&am…

【Spring】BeanFactory源碼翻譯

package org.springframework.beans.factory;import org.springframework.beans.BeansException; import org.springframework.core.ResolvableType; import org.springframework.lang.Nullable;/*** The root interface for accessing a Spring bean container.* 用于訪問Spri…

量化交易:Dual Thrust策略

哈嘍&#xff0c;大家好&#xff0c;我是木頭左&#xff01; Dual Thrust策略起源于20世紀80年代&#xff0c;由美國著名交易員和金融作家Larry Williams首次提出。這一策略的核心思想是通過捕捉市場中的短期波動來實現盈利。Larry Williams通過多年的研究和實踐&#xff0c;發…

智能EDM郵件群發工具哪個好?

企業之間的競爭日益激烈&#xff0c;如何高效、精準地觸達目標客戶&#xff0c;成為每個市場戰略家必須面對的挑戰。在此背景下&#xff0c;云銜科技憑借其前沿的AI技術和深厚的行業洞察&#xff0c;匠心推出了全方位一站式智能EDM郵件營銷服務平臺&#xff0c;重新定義了郵件營…

[ECE] SRAM DRAM

SRAM&#xff08;Static Random-Access Memory&#xff0c;靜態隨機存取存儲器&#xff09;和DRAM&#xff08;Dynamic Random-Access Memory&#xff0c;動態隨機存取存儲器&#xff09;是兩種主要的隨機存取存儲器技術&#xff0c;它們在計算機和其他電子設備中扮演著重要的角…

2024OD機試卷-字符串序列判定 (java\python\c++)

題目:字符串序列判定 題目描述 輸入兩個字符串 S 和 L ,都只包含英文小寫字母。S長度 ≤ 100,L長度 ≤ 500,000。判定S是否是L的有效子串。 判定規則:S 中的每個字符在 L 中都能找到(可以不連續),且 S 在L中字符的前后順序與 S 中順序要保持一致。(例如,S = ” ace…

StringBuilder

demo1 描述&#xff1a; 主要演示了StringBuilder類的使用。 首先創建一個空的StringBuilder對象s。 使用System.out.println()方法打印對象s&#xff0c;輸出結果為""&#xff08;空字符串&#xff09;。 調用StringBuilder的append()方法多次&#xff0c;將字符串…

半小時搞懂STM32面經知識——RCC

1. 時鐘的概念 時鐘是由電路產生的具有周期性的脈沖信號&#xff0c;相當于單片機的心臟&#xff0c;要想使用單片機的外設必須開啟時鐘。 時鐘對單片機有什么作用&#xff1f; 1. 驅動外設的本質是寄存器&#xff0c;而寄存器需要時鐘觸發才能改寫值。 2. 時鐘頻率越高&#…

安全風險 - 如何解決 setAccessible(true) 帶來的安全風險?

可能每款成熟的金融app上架前都會經過層層安全檢測才能執行上架&#xff0c;所以我隔三差五就能看到安全檢測報告中提到的問題&#xff0c;根據問題的不同級別&#xff0c;處理的優先級也有所不同&#xff0c;此次講的主要是一個 “輕度問題” &#xff0c;個人認為屬于那種可改…

FinnConverter格式轉換工具

FinnConverter簡介 1. 簡潔的操作界面 2. 支持多種格式相互轉換 支持word轉pdf&#xff1b;ppt轉pdf&#xff1b;raw格式轉png/jpng…&#xff1b;其他格式相互轉換 2.1 輸入格式支持 bmp、cr2、cr3、crw、cur、dcr、dng、doc、docx、gif、ico、jpeg、jpg、kdc、mos、nef、…

線程縱橫:C++并發編程的深度解析與實踐

hello &#xff01;大家好呀&#xff01; 歡迎大家來到我的Linux高性能服務器編程系列之《線程縱橫&#xff1a;C并發編程的深度解析與實踐》&#xff0c;在這篇文章中&#xff0c;你將會學習到C新特性&#xff0c;并發編程&#xff0c;以及其如何帶來的高性能的魅力&#xff0…

LeetCode hot100-39-N

101. 對稱二叉樹給你一個二叉樹的根節點 root &#xff0c; 檢查它是否軸對稱。做不出來哇&#xff0c;遞歸一生之敵 普通的對一棵樹的遞歸遍歷根本沒辦法只接比較左子樹的左和右子樹的右這樣來比較&#xff0c;所以這題比較巧妙的是把這棵樹當做兩棵樹一樣去遍歷比較。 官方…

使用XxlCrawler抓取全球航空公司ICAO三字碼

目錄 前言 一、數據源介紹 1、目標網站 2、頁面渲染結構 二、XxlCrawler信息獲取 1、創建XxlCrawler對象 2、定義PageVo對象 3、直接PageVO解析 4、自定義解析 總結 前言 長距離旅行或者出差&#xff0c;飛機一定是出行的必備方式。對于旅行達人或者出差人員而言&…

中國目前比較有影響力的人物顏廷利:物質與無知通音

既然是在中國優秀傳統文化之根-漢語當中&#xff0c;漢字‘物質’二字跟‘無知’通音&#xff0c;因此&#xff0c;面對當前金錢肆虐、物欲橫流的現實生活&#xff0c;當人類眾生把‘物質’&#xff08;無知&#xff09;生活看的太真、太重時&#xff0c;那么&#xff0c;這就很…