Xiaohong Wang

198 papers A* 2B 3C 14Misc 2Journal 113Unranked 62
YearRankTypeTitle / Venue / Authors
2026 J jnl
IEEE J. Biomed. Health Informatics
Hongyu Liang, Yongxuan Wang, Le Yang, Meimei Wu, Dan Wang, Xiaohong Wang, Rong Liu
2026 J jnl
Reliab. Eng. Syst. Saf.
Hui Tang, Lizhi Wang, Jie Wang, Xiaohong Wang, Minze Xu, Tielin Ma
2025 J jnl
IEEE Trans. Ind. Electron.
Shaohua Wang, Pengjie Huang, Ying Luo, Xiaohong Wang, Xin Luo
2025 conf
WF-IoT
Ziyang Ou, Zerui Xu, Xiangyu Zhao, Xiaohong Wang
2025 J jnl
IEEE J. Biomed. Health Informatics
Xiaohong Wang, Junmengyang Zhang, Xuefen Teng, Kok Wei Aik, Larry Natividad, Charmaine Cheng, Abigail Pui Choo Wong, Keng Yung Jih Felix, Angela S. Koh, Weimin Huang
2025 conf
WF-IoT
Zerui Xu, Ziyang Ou, Xiangyu Zhao, Xiaohong Wang
2025 J jnl
IEEE Trans. Ind. Informatics
Qinglei Kong, Songnian Zhang, Shuna Wen, Bo Chen, Xiaohong Wang
2025 J jnl
Array
Fang Luo, Weixiong Zhang, Xiaohong Wang, Zijiao Sun, Qiming Zhang
2025 J jnl
IEEE Commun. Lett.
Yunwei Zhang, Lingxin Zeng, Xiaohong Wang, Yong Gao
2025 J jnl
Neurocomputing
Yongsheng Dong, Hongjie Zhou, Lintao Zheng, Xiaohong Wang, Jinwen Ma
2025 J jnl
Syst.
Linlin Liu, Xiaohong Wang, Liqing Tang, Zhaoxuan Sun, Xue Wang
2025 J jnl
Scientometrics
Xiaohong Wang, Jiyang Zhao
2025 J jnl
CoRR
Yi Zhang, Yushen Long, Yun Ni, Liping Huang, Xiaohong Wang, Jun Liu
2025 J jnl
J. Big Data
Xiaohong Wang, Zhenyu Wang
2025 J jnl
BMC Medical Imaging
Bowen Yang, Renbing Zhou, Xiaohong Wang, Yan Li, Panxia Wang, Yue Hao, Wenwen Li, Lei Zhang, Wenjing Su, Jie Qin, Ya Qiu, Junyang Luo
2025 B conf
SMC
Yushan Teng, Xiaohong Wang, Lizhi Wang, Ruyue Li
2025 J jnl
Technol. Anal. Strateg. Manag.
Xiangyu Luan, Xiaohong Wang, Na Li
2025 J jnl
IEEE Trans. Control. Syst. Technol.
Pengchong Chen, Ying Luo, Li Zhang, Xiaohong Wang, YangQuan Chen
2025 J jnl
Trans. Inst. Meas. Control
Weijia Zheng, Xiaorong Li, YangQuan Chen, Ze-Hao Wu, Xiaohong Wang
2025 J jnl
J. Supercomput.
Xu Zhao, Xiaohong Wang, Bingge Cong
2025 J jnl
Syst. Eng.
Hui Tang, Lizhi Wang, Yaning Sun, Xiaohong Wang, Tielin Ma
2025 J jnl
Symmetry
Chao Wu, Xiaohong Wang, Yao Ren, Yuying Zhou
2025 J jnl
Scientometrics
Xiaohong Wang, Jiyang Zhao, Ben Zhang
2025 J jnl
J. Theor. Appl. Electron. Commer. Res.
Yanyan Ma, Xiaohong Wang, Yixuan Kang, Linlin Liu
2025 J jnl
J. Imaging Inform. Medicine
Xiaohong Wang, Zhongkang Lu, Su Huang, Yonghan Ting, Jordan Zheng Ting Sim, Wenxiang Chen, Cher Heng Tan, Weimin Huang
2024 conf
MESA
He Gan, Shaohua Wang, Ying Luo, Xiaohong Wang, YangQuan Chen
2024 conf
MESA
Shaohua Wang, He Gan, Ying Luo, Xiaohong Wang, YangQuan Chen
2024 J jnl
IEEE Trans. Ind. Electron.
Likai Zheng, Quanxue Guan, Xiaohong Wang, Ying Luo, Yuxiang Wu
2024 conf
EMBC
Ruixiang Pan, Xiaohong Wang, Zhiping Lin, Chi Longn Ho, Oliver James Nickalls, Cynthia Assimta Peter, Donovan Tay, Weimin Huang
2024 J jnl
IEEE Trans. Circuits Syst. I Regul. Pap.
Dongping Zhen, Zerui Xu, Jiajie Yan, Yuan Tu, Changming Ding, Chi Zhang, She Chen, Lei Shan, Xiaohong Wang, Sixing Xu
2024 J jnl
Graph. Model.
Kun Zhang, Ao Zhang, Xiaohong Wang, Weisong Li
2024 J jnl
Int. J. Imaging Syst. Technol.
Zhanao Meng, Qing Xiang, Jian Cao, Yahao Guo, Sisi Deng, Tao Luo, Yue Zhang, Ke Zhang, Xuan Zhu, Kun Ma, Xiaohong Wang, Jie Qin
2024 J jnl
IEEE Trans. Geosci. Remote. Sens.
Jiangmin Tian, Chenxing Wang, Jiuwen Cao, Xiaohong Wang
2024 conf
EMBC
Jiangyun Li, Zhongkang Lu, Shuang Leng, Xiaohong Wang, Lohendran Baskaran, Min Sen Yew, Mark Chan, Lynette L. Teo, Kee Yuan Ngiam, Hwee Kuan Lee, Liang Zhong, Zhiping Lin, Weimin Huang
2024 J jnl
J. Intell. Manuf.
Chuanhao Wang, Yongjian Sun, Xiaohong Wang
2024 J jnl
Multim. Tools Appl.
Xiaohong Wang, Yanxiu Zhai, Xiangcai Ma, Jing Zeng, Youci Liang
2024 J jnl
J. Supercomput.
Xiaohong Wang, Xu Zhao, Kun Xu, Shihao Xu
2024 J jnl
Reliab. Eng. Syst. Saf.
Linghao Kong, Lizhi Wang, Zhongzheng Cao, Xiaohong Wang
2024 J jnl
IEEE Access
Zijiao Sun, Xiaohong Wang, Fang Luo, Zhiliang Zhang, Yanghui Li
2024 conf
ICCEIC
Kaiyuan Xu, Xiaohong Wang, Rongfeng Zhang, Jinguo Sang
2023 J jnl
IEEE Trans. Circuits Syst. I Regul. Pap.
Jingyu Weng, Xiaojun Bi, Zilan Cao, Xiaohong Wang, Qinfen Xu
2023 J jnl
Comput. Syst. Sci. Eng.
Xiaohong Wang, Huiqing Wu, Yuying Ma, Shuzhen Huang
2023 J jnl
Trans. Inst. Meas. Control
Rongfeng Zhang, Shizeng Lu, Xiaohong Wang, Hongliang Yu, Zhao Liu
2023 conf
DSA
Hui Tang, Lizhi Wang, Lingfei You, Mengfei Yao, Xiaohong Wang
2023 J jnl
Sci. China Inf. Sci.
Xiaohong Wang, Zhen Wang, Jianwei Xia, Hao Shen, Yuxia Li
2023 J jnl
BMC Bioinform.
Xiaohong Wang, Xiaoli Jing, Fangkun Dou, Haowei Cao
2023 J jnl
Syst.
Ximeng Chen, Xiaohong Wang, Yanzhang Qu
2023 conf
EMBC
Xiaohong Wang, Shuang Leng, Zhongkang Lu, Su Huang, Beng Hai Lee, Lohendran Baskaran, Min Sen Yew, Lynette Li San Teo, M. Y. Chan, Kee Yuan Ngiam, Hwee Kuan Lee, Liang Zhong, Weimin Huang
2023 J jnl
IEEE Access
Rongli Gai, Xiaohong Wang, Kang Wang
2023 J jnl
Sensors
Kun Zhang, Rui Chen, Zidong Peng, Yawei Zhu, Xiaohong Wang
2023 J jnl
Circuits Syst. Signal Process.
Lina Jin, Guoyou Shi, Shuanghe Yu, Xiaohong Wang
2023 J jnl
IEEE Trans. Syst. Man Cybern. Syst.
Kezhen Han, Jian Feng, Yueyang Li, Ping Jiang, Xiaohong Wang
2023 C conf
iSPEC
Guoning Wang, Xiaohong Wang, Jinli Wu, Xiaochun Mou, Weimei Ge, Zhigang Wang
2023 J jnl
Bus. Process. Manag. J.
Xiangyu Luan, Xiaohong Wang
2023 J jnl
IEEE Access
Hengchao Ma, Zhao Liu, Xiaohong Wang, Honghao Ma
2023 conf
IWBBIO (1)
Werner E. G. Müller, Shunfeng Wang, Meik Neufurth, Heinz C. Schröder, Xiaohong Wang
2023 J jnl
IEEE Access
Rongfeng Zhang, Xiaohong Wang, Hongliang Yu
2023 J jnl
Entropy
Lihong Guan, Xiaohong Wang
2022 conf
MLMI@MICCAI
Peter Ho Hin Yuen, Xiaohong Wang, Zhiping Lin, Nikki Ka Wai Chow, Jun Cheng, Cher Heng Tan, Weimin Huang
2022 J jnl
CoRR
Xiaohong Wang, Chaoqi Li, Xiangcai Ma
2022 J jnl
IEEE Trans. Geosci. Remote. Sens.
Chenxing Wang, Jiangmin Tian, Jiuwen Cao, Xiaohong Wang
2022 conf
RICAI
Jinchuan Huang, Shufan Yang, Xiaohong Wang
2022 conf
iThings/GreenCom/CPSCom/SmartData/Cybermatics
Rongli Gai, Xiaohong Wang, Zhiyuan Chang, Yitong Guo
2022 conf
ICBDE
Xiaohong Wang, Yanzheng Wang
2022 C conf
IPCCC
Yuxin Yuan, Xiao Shi, Zhengyu Lei, Xiaohong Wang, Xiaofang Zhao
2022 conf
ML4CS (2)
Teng Peng, Xiaohong Wang
2022 J jnl
Phys. Commun.
Jiangfeng Sun, Xiaohong Wang, Xinji Tian, Yin Wu, Xingwang Li
2022 J jnl
Comput. Biol. Medicine
Shuhang Wang, Vivek Kumar Singh, Eugene Cheah, Xiaohong Wang, Qian Li, Shinn-Huey Chou, Constance D. Lehman, Viksit Kumar, Anthony E. Samir
2022 J jnl
Technol. Anal. Strateg. Manag.
Shaopeng Zhang, Xiaohong Wang, Ben Zhang
2022 C conf
NPC
Wenting Tan, Xiao Shi, Zhengyu Lei, Dong Liang, Cunchi Lv, Xiaohong Wang, Xiaofang Zhao
2021 J jnl
Entropy
Weijia Zheng, Ying Luo, YangQuan Chen, Xiaohong Wang
2021 J jnl
IEEE Access
Yao Wang, Siyu Ji, Tianshun Yang, Xiaohong Wang, Huiquan Wang, Xiaoyun Zhao, Jinhai Wang
2021 J jnl
IEEE Access
Yao Wang, Siyu Ji, Tianshun Yang, Xiaohong Wang, Huiquan Wang, Xiaoyun Zhao
2021 conf
ICEMT
Yanzheng Wang, Xiaohong Wang
2021 J jnl
Int. J. Imaging Syst. Technol.
Weimin Liu, Meijun Long, Lingrong Peng, Caihong Qu, Ruomi Guo, Zhuang Kang, Jin Wang, Juekun Wu, Xiaohong Wang
2021 J jnl
Scientometrics
Shaopeng Zhang, Xiaohong Wang
2021 J jnl
Commun. Stat. Simul. Comput.
Xiaohong Wang, Dehui Wang, Kai Yang, Da Xu
2021 J jnl
Int. J. Inf. Technol. Syst. Approach
Xiaohong Wang
2021 J jnl
Trans. Emerg. Telecommun. Technol.
Kun Zhao, Xiaoxian Yang, Wei Xi, Zhiping Jiang, Xiaohong Wang, Zhi Wang, Xinyuan Ji, Zhao Yang, Jizhong Zhao
2021 conf
NEMS
Dawei Wang, Chennan Lu, Xiaohong Wang, Wei Rao
2021 conf
ICCMA
Xiaohong Wang, Chuntai Wu, Zhi Feng Pan, Lian-Fang Tian
2021 J jnl
Signal Image Video Process.
Xiaohong Wang, Fang Liu, Xiangcai Ma
2021 conf
EMBC
Xiaohong Wang, Weimin Huang, Zhongkang Lu, Su Huang
2021 J jnl
IEEE Trans. Ind. Informatics
Qinglei Kong, Feng Yin, Rongxing Lu, Beibei Li, Xiaohong Wang, Shuguang Cui, Ping Zhang
2021 J jnl
Neural Process. Lett.
Xiaohong Wang, Zhen Wang, Jianwei Xia, Qian Ma
2021 J jnl
IEEE Trans. Ind. Electron.
Kezhen Han, Jian Feng, Qing Zhao, Ping Jiang, Xiaohong Wang
2021 J jnl
J. Knowl. Manag.
Xueyan Zhang, Xiaohong Wang
2021 conf
ICBDE
Xiaohong Wang, Yanzheng Wang
2020 J jnl
Neural Networks
Xiaohong Wang, Zhen Wang, Qiankun Song, Hao Shen, Xia Huang
2020 J jnl
J. Multiple Valued Log. Soft Comput.
Congmu Yu, Xiaohong Wang, Chuyuan Zhang
2020 J jnl
J. Intell. Fuzzy Syst.
Xiaohong Wang, Huaiqin Wu, Jinde Cao
2020 conf
ICCA
Wei Wang, Chunyan Han, Xiaohong Wang
2020 conf
ICCA
Wei Wang, Chunyan Han, Xiaohong Wang
2020 J jnl
IEEE Access
Yusen Geng, Xiaohong Wang, Ping Jiang
2020 J jnl
Multim. Tools Appl.
Xiaohong Wang, Yunjie Pang, Xiangcai Ma
2020 conf
MLHC
Szu-Yeu Hu, Shuhang Wang, Wei-Hung Weng, Jingchao Wang, Xiaohong Wang, Arinc Ozturk, Quan Li, Viksit Kumar, Anthony E. Samir
2020 J jnl
CoRR
Shuhang Wang, Szu-Yeu Hu, Eugene Cheah, Xiaohong Wang, Jingchao Wang, Lei Chen, Masoud Baikpour, Arinc Ozturk, Qian Li, Shinn-Huey Chou, Constance D. Lehman, Viksit Kumar, Anthony E. Samir
2020 J jnl
CoRR
Szu-Yeu Hu, Shuhang Wang, Wei-Hung Weng, Jingchao Wang, Xiaohong Wang, Arinc Ozturk, Qian Li, Viksit Kumar, Anthony E. Samir
2019 C conf
ICIS
Qilong Li, Xiaohong Wang
2019 J jnl
Remote. Sens.
Ying Yu, Weihang Zeng, Wen Liu, He Zhang, Xiaohong Wang
2019 J jnl
IEEE Access
Zhi Feng Pan, Xiaohong Wang, Thi Thu Giang Hoang, Zijiang Wang, Lian-Fang Tian
2019 J jnl
J. Medical Syst.
Xiaohong Wang, Na Zhao, Jingjing Shi, Yuhua Wu, Jun Liu, Qiang Xiao, Jian Hu
2019 J jnl
J. Medical Syst.
Xiaohong Wang, Na Zhao, Jingjing Shi, Yuhua Wu, Jun Liu, Qiang Xiao, Jian Hu
2019 J jnl
Neurocomputing
Fanjun Li, Xiaohong Wang, Ying Li
2019 conf
ISIE
Kezhen Han, Ping Jiang, Shuangxi Huang, Yuan Jiang, Xiaohong Wang
2019 J jnl
Appl. Math. Comput.
Xiaohong Wang, Haibo Du, Shihua Li, Xingpeng Zhou
2019 C conf
ICIS
Jiadong Guo, Xiaohong Wang
2019 J jnl
Mob. Inf. Syst.
Zhiliang Zhang, Ying Liu, Fang Luo, Xiaohong Wang
2019 J jnl
IEEE Access
Haining Liu, Yixiang Wang, Fajia Li, Xiaohong Wang, Chengliang Liu, Michael G. Pecht
2019 J jnl
IEEE Access
Tonghua Wu, Yuping Zheng, Hongbin Wu, Hao Dong, Xiaohong Wang
2019 J jnl
J. Robotics Mechatronics
Ping Jiang, Hongliang Yu, Shi Li, Xiaohong Wang
2019 conf
CSIA
Xiaohong Wang
2019 J jnl
Int. J. Intell. Inf. Technol.
Hongyan Yun, Ying He, Li Lin, Xiaohong Wang
2019 J jnl
J. Medical Syst.
Xiaohong Wang, Na Zhao, Peng Ouyang, Jiayi Lin, Jian Hu
2019 J jnl
Sensors
Chengcheng Li, Delun Chen, Yuanyuan Wang, Xiaoyong Lai, Juan Peng, Xiaohong Wang, Kexi Zhang, Yang Cao
2019 J jnl
IEEE Access
Xiaohong Wang, Zhen Wang, Xianggeng Zhu, Bo Meng, Jianwei Xia
2019 J jnl
Trans. Inst. Meas. Control
Shizeng Lu, Hongliang Yu, Xiaohong Wang
2018 C conf
ACC
Yongjian Sun, Xiaohong Wang
2018 C conf
ICIS
Qilong Li, Xiaohong Wang
2018 J jnl
J. Electron. Commer. Organ.
Liwei Li, Xiaohong Wang
2018 J jnl
Vis. Comput.
Yongsheng Dong, Jinwang Feng, Chunlei Yang, Xiaohong Wang, Lintao Zheng, Jiexin Pu
2018 J jnl
Int. J. Syst. Sci.
Zhonghua Liu, Gang Liu, Jiexin Pu, Xiaohong Wang, Haijun Wang
2018 J jnl
Eng. Appl. Artif. Intell.
Shizeng Lu, Hongliang Yu, Huijun Dong, Xiaohong Wang, Yongjian Sun
2018 conf
ICCCS (6)
Zhao Yang, Wei Xi, Kun Zhao, Xiaohong Wang, Colin Allen, Jizhong Zhao
2018 C conf
ICIS
Zhengkang Chen, Xiaohong Wang
2018 J jnl
Sensors
Jia Wang, Hina Mukhtar, Lan Ma, Qian Pang, Xiaohong Wang
2017 conf
ICIIP
Haiyao Wang, Jingyang Wang, Xiaohong Wang
2017 J jnl
Scientometrics
Ben Zhang, Xiaohong Wang
2017 J jnl
Complex.
Guodong Liu, Xiaohong Wang, Xinzhu Meng, Shujing Gao
2017 J jnl
Knowl. Based Syst.
Zhonghua Liu, Xiaohong Wang, Jiexin Pu, Lin Wang, Lin Zhang
2017 conf
ACIT/CSII/BCD
Lu Jiang, Xiaohong Wang, Ligu Zhu
2017 J jnl
Vis. Comput.
Chunlei Yang, Jiexin Pu, Yongsheng Dong, Zhonghua Liu, Lingfei Liang, Xiaohong Wang
2017 J jnl
Int. J. Bifurc. Chaos
Zhen Wang, Xiaohong Wang, Yuxia Li, Xia Huang
2017 J jnl
CoRR
Colin Allen, Hongliang Luo, Jaimie Murdock, Jianghuai Pu, Xiaohong Wang, Yanjie Zhai, Kun Zhao
2016 C conf
ICIS
Cheng Feng, Xiaohong Wang
2016 conf
ICIA
Xiaohong Wang, Zhumu Fu, Jiexin Pu, Wenshao Bu
2016 J jnl
Microelectron. Reliab.
Erjuan Guo, Zhigang Zeng, Yan Zhang, Xiao Long, Haijun Zhou, Xiaohong Wang
2015 conf
ICIA
Wenshao Bu, Ziyuan Li, Xiaohong Wang, Xiaoqiang Li
2015 conf
ICIA
Xiaohong Wang, Jiexin Pu, Zhumu Fu, Xingjun Chen
2015 conf
UIC/ATC/ScalCom
Chuanzi Tang, Hongmei Ren, Zhihe Xiao, Xiaohong Wang
2015 J jnl
IEEE Trans. Auton. Ment. Dev.
Yang Liu, Yan Liu, Chaoguang Wang, Xiaohong Wang, Pei-Yuan Zhou, Gino Yu, Keith C. C. Chan
2014 conf
CCTA
Xiaohong Wang, Zhuo Zhao, Yuhe Wang, Jianhan Lin
2014 J jnl
J. Frankl. Inst.
Jun Mei, Minghui Jiang, Xiaohong Wang, Jiali Han, Shuangtao Wang
2014 conf
ICIA
Xiaohong Wang, Xuhui Zhao, Jiexin Pu, Wenshao Bu, Xingjun Chen
2014 J jnl
J. Softw.
Xiaohong Wang, Jianping Wu
2013 Misc conf
ICNC
Xiaohong Wang, Qinghua Su, Yongfei Miao
2013 J jnl
J. Cheminformatics
Sheng Tian, Youyong Li, Junmei Wang, Xiaojie Xu, Lei Xu, Xiaohong Wang, Lei Chen, Tingjun Hou
2013 conf
ISNN (1)
Xiaohong Wang, Xingjun Chen, Huan Qi
2013 J jnl
Int. J. Comput. Intell. Syst.
Xiaohong Wang, Huan Qi
2013 C conf
IECON
Quanxue Guan, Ping Yang, Quansheng Guan, Xiaohong Wang
2012 conf
NEMS
Siwei Li, Xiaohong Wang, Caiwei Shen, Jian'gan Wang, Feiyu Kang
2012 conf
FSKD
Qinghua Su, Zhangcan Huang, Xiaohong Wang, Zongbo Hu
2012 J jnl
Autom.
Tao Shen, Zhugang Yuan, Xiaohong Wang
2012 conf
FSKD
Qinghua Su, Zhangcan Huang, Zhongbo Hu, Xiaohong Wang
2012 C conf
ICARCV
Xiaohong Wang, Hui Li, Qingjin Meng
2012 J jnl
J. Appl. Math.
Yakui Xue, Xiaohong Wang
2012 C conf
ICARCV
Xiaohong Wang, Shizeng Lu, Shaohong Jing
2012 conf
ICSI (1)
Xiaohong Wang, Huan Qi
2012 conf
ICAL
Na Liu, Shaohong Jing, Xiaohong Wang
2012 J jnl
Autom.
Tao Shen, Zhugang Yuan, Xiaohong Wang
2011 conf
CSEE (1)
Daohe Chen, Xiaohong Wang, Wenze Li
2011 J jnl
J. Comput.
Guoli Wang, Jianhui Wu, Jianhua Wu, Xiaohong Wang
2011 J jnl
Autom.
Tao Shen, Zhugang Yuan, Xiaohong Wang
2011 J jnl
Comput. Biol. Medicine
Xiaohong Wang, Xiaoyang Li
2011 J jnl
Comput. Biol. Medicine
Xiaohong Wang, Xiaoyang Li
2011 ch.
Graph Data Management
Xiaohong Wang, Jun Huan
2011 conf
NEMS
Mei Wang, Xiaohong Wang, Shan Wu, Zhimin Tan, Litian Liu, Xun Guo
2010 conf
IWACI
Minghui Jiang, Shengle Fang, Xiaohong Wang
2010 J jnl
BMC Bioinform.
Xiaohong Wang, Jun Huan, Aaron M. Smalter, Gerald H. Lushington
2010 conf
DBTA
Suyin Dong, Min Huang, Ying Liu, Jingyang Wang, Xiaohong Wang
2010 conf
ISNN (1)
Xiaohong Wang, Minghui Jiang, Chuntao Jiang, Shengrong Li
2010 conf
WISM
Xiaohong Wang, Yilei Pei, Liwei Li
2010 J jnl
Autom.
Tao Shen, Xiaohong Wang, Zhugang Yuan
2009 conf
BIBM
Xiaohong Wang, Jun Huan, Aaron M. Smalter, Gerald H. Lushington
2009 conf
VECIMS
Tongkui Cui, Yongnian Yan, Renji Zhang, Xiaohong Wang
2009 J jnl
Int. J. Asian Lang. Process.
Buzhou Tang, Xuan Wang, Xiaohong Wang
2009 B conf
EDBT
Xiaohong Wang, Aaron M. Smalter, Jun Huan, Gerald H. Lushington
2009 conf
ICECS
Qiurong Wang, Ningshe Zhao, Xiaohong Wang
2009 conf
HIS (2)
Shaohong Jing, Xiaohong Wang
2009 conf
VECIMS
Mingen Xu, Yongnian Yan, Xiaohong Wang
2009 conf
HIS (2)
Hongliang Yu, Xiaohong Wang, Zhugang Yuan
2009 conf
IFITA (3)
Xiaohong Wang, Li Liu
2008 conf
ICNC (4)
Xinli Fang, Tao Shen, Xiaohong Wang, Zhiqun Zhou
2008 conf
CCTA (3)
Xiaohong Wang, Baofeng Chen
2007 conf
ISNN (1)
Minghui Jiang, Xiaohong Wang, Yi Shen
2006 conf
ISDA (1)
Xiaohong Wang, Hongliang Yu, Xinhe Xu
2006 C conf
ICARCV
Hongliang Yu, Xinhe Xu, Xiaohong Wang
2006 conf
ISNN (1)
Xiaohong Wang, Sitao Wu, Xiaoru Wang, Qunzhan Li
2005 conf
ISNN (3)
Sitao Wu, Xiaohong Wang
2005 conf
Software Engineering Research and Practice
Xiaohong Wang
2005 B conf
LCN
Kai Wang, Jing Huang, Zhongcheng Li, Xiaohong Wang, Feng Yang, Jingping Bi
2005 C conf
ISADS
Xiaohong Wang, Sitao Wu, Qunzhan Li, Xiaoru Wang
2002 Misc conf
PDPTA
Xiaohong Wang, Sanzheng Qiao
2001 A* conf
ICDE
Gösta Grahne, Laks V. S. Lakshmanan, Xiaohong Wang, Ming Hao Xie
2000 A* conf
ICDE
Gösta Grahne, Laks V. S. Lakshmanan, Xiaohong Wang
1999 conf
1999 ACM SIGMOD Workshop on Research Issues in Data Mining and Knowledge Discovery
Gösta Grahne, Laks V. S. Lakshmanan, Xiaohong Wang
1998
Xiaohong Wang
1995 conf
ICIP (3)
Xiaohong Wang, Steven D. Blostein
s3-storage/s3_uploader.py
← Index s3-storage/s3_uploader.py python
# python s3_uploader.py /path/to/files repository_name --notes "Optional notes"
import hashlib
import multiprocessing
from multiprocessing import Pool
import sys
import logging
from logging.handlers import QueueHandler
from datetime import datetime
import os
import time
import json
from enum import Enum
import tempfile
import magic
from typing import Optional, Dict, Any
from dotenv import load_dotenv
from datetime import timezone

# File handling
import py7zr
import pyzipper
from magika import Magika

# S3 and database
from minio import Minio
import clickhouse_connect

load_dotenv(override=True)

class UploadResult(Enum):
    CORRECTLY = 0
    FAILED = 1
    SKIPPED = 2  # For duplicates

class FileNameFormatter(logging.Formatter):
    def format(self, record):
        record.filenameinfo = getattr(record, "filenameinfo", "unknown")
        return super().format(record)

def setup_logger(log_file, filename):
    """Set up a logger for a specific file"""
    logger = logging.getLogger(filename)
    if not logger.handlers:
        if os.getenv("SERVER_ENV") == "prod":
            logger.setLevel(logging.INFO)
        else:
            logger.setLevel(logging.DEBUG)
            
        # Create file handler
        handler = logging.FileHandler(log_file)
        formatter = FileNameFormatter(
            "%(asctime)s - %(filenameinfo)s - %(levelname)s - %(message)s"
        )
        handler.setFormatter(formatter)
        logger.addHandler(handler)
        logger.propagate = False
    return logger

def logger_thread(log_queue, log_file):
    handler = logging.FileHandler(log_file)
    formatter = FileNameFormatter(
        "%(asctime)s - %(filenameinfo)s - %(levelname)s - %(message)s"
    )
    handler.setFormatter(formatter)

    while True:
        try:
            record = log_queue.get()
            if record is None:
                break
            message = formatter.format(record)
            handler.stream.write(message + "\n")
            handler.stream.flush()
        except Exception:
            import traceback
            print("[ERR] Error in logger thread:", file=sys.stderr)
            traceback.print_exc(file=sys.stderr)

def get_s3_key(sha256: str, is_archived: bool, original_ext: str = None) -> str:
    """
    Generate S3 key using sharding pattern with appropriate extension
    
    Args:
        sha256: The file's SHA256 hash
        is_archived: Whether the file was already archived (zip/7z)
        original_ext: The original file extension (if any)
    """
    # If file was already archived, keep its extension
    if is_archived and original_ext and original_ext.lower() in ['.zip', '.7z']:
        extension = original_ext
    else:
        # For files we archived ourselves, use .zip
        extension = '.zip'
        
    return f"{sha256[:2]}/{sha256[2:4]}/{sha256}{extension}"

def process_single_file(args):
    """Independent worker function for processing a single file"""
    filepath, file_number, total_files, config, log_file = args
    filename = os.path.basename(filepath)
    base_logger = setup_logger(log_file, filename)
    extra = {"filenameinfo": filename}
    logger = logging.LoggerAdapter(base_logger, extra)
    s3_client = None
    ch_client = None

    try:
        logger.info(f"Processing file {file_number}/{total_files}: {filepath}")

        # Create connections for this process
        s3_client = Minio(
            config["s3_endpoint"],
            access_key=config["s3_access_key"],
            secret_key=config["s3_secret_key"],
            secure=True
        )
        
        ch_client = clickhouse_connect.get_client(
            host=config['clickhouse_host'],
            port=config['clickhouse_port'],
            username=config['clickhouse_user'],
            password=config['clickhouse_password'],
            database=config['clickhouse_database'],
            verify=config['clickhouse_verify']
        )

        # Process the file and get contents
        ext = os.path.splitext(filepath.lower())[1]
        is_archived = ext in ['.zip', '.7z']

        with tempfile.TemporaryDirectory() as temp_dir:
            try:
                if filename.startswith("."):
                    logger.debug(f"Skipping hidden file: {filename}")
                    return UploadResult.SKIPPED

                elif ext == ".zip":
                    logger.debug(f"Processing ZIP file: {filepath}")
                    with pyzipper.AESZipFile(filepath) as zf:
                        zf.pwd = b"infected"
                        filename = zf.namelist()[0]
                        zf.extractall(temp_dir)
                        extracted_path = os.path.join(temp_dir, filename)
                        
                        with open(extracted_path, "rb") as f:
                            file_content = f.read()
                        with open(filepath, "rb") as f:
                            archive_content = f.read()
                            
                elif ext == ".7z":
                    logger.debug(f"Processing 7z file: {filepath}")
                    with py7zr.SevenZipFile(filepath, mode="r", password="infected") as z:
                        z.extractall(path=temp_dir)
                        # Get first file in the archive
                        for root, _, files in os.walk(temp_dir):
                            if files:
                                filename = files[0]
                                extracted_path = os.path.join(root, filename)
                                break
                                
                        with open(extracted_path, "rb") as f:
                            file_content = f.read()
                        with open(filepath, "rb") as f:
                            archive_content = f.read()
                else:
                    logger.debug(f"Processing non-archived file: {filepath}")
                    # For non-archived files, create password protected zip
                    with open(filepath, "rb") as f:
                        file_content = f.read()
                    filename = os.path.basename(filepath)
                    
                    # Create password protected zip
                    with tempfile.NamedTemporaryFile(delete=False) as temp_zip:
                        with pyzipper.AESZipFile(
                            temp_zip.name,
                            'w',
                            compression=pyzipper.ZIP_LZMA,
                            encryption=pyzipper.WZ_AES
                        ) as zf:
                            zf.pwd = b'infected'
                            zf.writestr(filename, file_content)
                        
                        with open(temp_zip.name, 'rb') as f:
                            archive_content = f.read()
                    
                    os.unlink(temp_zip.name)

                # Calculate hash
                sha256 = hashlib.sha256(file_content).hexdigest()
                # s3_key = f"{sha256[:2]}/{sha256[2:4]}/{sha256}"
                s3_key = get_s3_key(sha256, is_archived, ext if is_archived else None)
                logger.debug(f"Calculated SHA256: {sha256}")

                # Check if exists in S3
                try:
                    s3_client.stat_object(config["s3_bucket"], s3_key)
                    exists = True
                    logger.debug(f"File exists in S3: {s3_key}")
                except:
                    exists = False
                    logger.debug(f"File does not exist in S3: {s3_key}")

                # Prepare metadata
                max_timestamp = int(datetime(9999, 12, 31, 23, 59, 59, tzinfo=timezone.utc).timestamp() * 1000)
                now = datetime.now(timezone.utc)
                now_timestamp = int(now.timestamp() * 1000)
                inverted_timestamp = datetime.fromtimestamp((max_timestamp - now_timestamp)/1000, tz=timezone.utc)


                data = [[
                    sha256,                     # String
                    filename,                   # String
                    config['repository'],       # String
                    len(file_content),         # UInt64
                    magic.from_buffer(file_content),        # String
                    magic.from_buffer(file_content, mime=True),  # String
                    Magika().identify_bytes(file_content).output.ct_label,  # String
                    now,                       # upload_date
                    now,                       # first_seen
                    inverted_timestamp,        # version_date for replacing
                    config['s3_bucket'],       # String
                    s3_key,                    # String
                    config['notes'] if config['notes'] else None  # Nullable(String)
                ]]

                column_names = [
                    'sha256', 'filename', 'repository', 'file_size',
                    'filetype', 'filetype_mime', 'filetype_magika',
                    'upload_date', 'first_seen', 'version_date', 's3_bucket', 's3_key', 'notes'
                ]

                column_type_names = [
                    'String', 'String', 'LowCardinality(String)', 'UInt64',
                    'String', 'String', 'String',
                    'DateTime64(3, \'UTC\')', 'DateTime64(3, \'UTC\')', 
                    'DateTime64(3, \'UTC\')', 'String', 'String', 'Nullable(String)'
                ]
                
                if exists:
                    # Just update database
                    try:
                        ch_client.insert(
                            'samples_catalog',
                            data,
                            column_names=column_names,
                            column_type_names=column_type_names,
                            settings={'input_format_values_interpret_expressions': 0}
                        )
                        logger.info(f"Updated metadata for existing file: {sha256}")
                    except Exception as e:
                        logger.error(f"Error inserting metadata: {str(e)}")
                        import traceback
                        logger.error(f"Full traceback: {traceback.format_exc()}")
                    return UploadResult.SKIPPED
                else:
                    try:    
                        # Upload to S3 and insert metadata
                        import io
                        file_data = io.BytesIO(archive_content)
                        s3_client.put_object(
                            bucket_name=config['s3_bucket'],
                                object_name=s3_key,
                                data=file_data,
                                length=len(archive_content)
                            )
                        logger.info(f"Uploaded to S3: {s3_key}")
                    except Exception as e:
                        logger.error(f"Error uploading to S3: {str(e)}")
                        import traceback
                        logger.error(f"Full traceback: {traceback.format_exc()}")
                        return UploadResult.FAILED
                    
                    try:
                        ch_client.insert(
                            'samples_catalog',
                            data,
                            column_names=column_names,
                            column_type_names=column_type_names,
                            settings={'input_format_values_interpret_expressions': 0}
                        )
                        logger.info(f"Successfully processed file: {sha256}")
                        return UploadResult.CORRECTLY
                    except Exception as e:
                        logger.error(f"Error inserting metadata: {str(e)}")
                        import traceback
                        logger.error(f"Full traceback: {traceback.format_exc()}")
                        return UploadResult.FAILED

            except Exception as e:
                logger.error(f"Error processing file: {str(e)}")
                import traceback
                logger.error(f"Full traceback: {traceback.format_exc()}")
                return UploadResult.FAILED

    except Exception as e:
        logger.error(f"Error in worker: {str(e)}")
        return UploadResult.FAILED
    finally:
        # if s3_client:
        #     s3_client.close()
        if ch_client:
            ch_client.close()
        if base_logger and base_logger.handlers:
            for handler in base_logger.handlers:
                handler.close()
            base_logger.handlers.clear()

class S3Uploader:
    def __init__(self, path: str, repository: str, notes: Optional[str] = None):
        """
        Initialize S3 uploader
        
        Args:
            path: Path to file or directory to process
            repository: Repository name
            notes: Optional notes to add to all files
        """
        self.path = path
        self.repository = repository
        self.notes = notes
        
        # Initialize multiprocessing components
        self.manager = multiprocessing.Manager()
        # self.log_queue = self.manager.Queue()
        self.total_results = self.manager.dict({result: 0 for result in UploadResult})
        
        # Set up logging
        self.today = datetime.today().strftime("%Y%m%dT%H%M%S")
        # self.log_file = os.getenv("LOG_FILE_PATH", "./logs/") + f"{self.today}-{self.repository}-upload.txt"
        log_base_path = os.getenv("LOG_FILE_PATH")
        if not log_base_path:
            log_base_path = os.path.join(os.getcwd(), "logs")  # Default to ./logs directory
        self.log_file = os.path.join(log_base_path, f"{self.today}-{self.repository}-upload.txt")
        os.makedirs(os.path.dirname(self.log_file), exist_ok=True)

        # Initialize S3 client
        self.s3_client = Minio(
            os.getenv("S3_ENDPOINT"),
            access_key=os.getenv("S3_ACCESS_KEY"),
            secret_key=os.getenv("S3_SECRET_KEY"),
            secure=True  # Set to False if not using HTTPS
        )
        self.bucket_name = os.getenv("S3_BUCKET")

        # Initialize ClickHouse client
        self.ch_client = clickhouse_connect.get_client(
            host=os.getenv('CLICKHOUSE_HOST'),
            port=os.getenv('CLICKHOUSE_PORT'),
            username=os.getenv('CLICKHOUSE_USER'),
            password=os.getenv('CLICKHOUSE_PASSWORD'),
            database=os.getenv('CLICKHOUSE_DATABASE'),
            verify=os.getenv('CLICKHOUSE_ENFORCE_SSL', 'False')
        )
        
        # Ensure database table exists
        self._create_table()

    def _create_table(self):
        """Create the file catalog table if it doesn't exist"""
        create_table_query = """
            CREATE TABLE IF NOT EXISTS samples_catalog (
                sha256 String,
                filename String,
                repository LowCardinality(String),
                file_size UInt64,
                filetype String,
                filetype_mime String,
                filetype_magika String,
                upload_date DateTime64(3, 'UTC'),
                first_seen DateTime64(3, 'UTC'),
                version_date DateTime64(3, 'UTC'),  -- This will be inverted timestamp
                s3_bucket String,
                s3_key String,
                notes Nullable(String),
                PRIMARY KEY (sha256, repository)
            ) ENGINE = ReplacingMergeTree(version_date)
            ORDER BY (sha256, repository)
        """
        self.ch_client.command(create_table_query)

    # # def upload(self):
    # #     """Main upload function with multiprocessing support"""
    # #     start_time = time.time()

    # #     config = {
    # #         "s3_endpoint": os.getenv("S3_ENDPOINT"),
    # #         "s3_access_key": os.getenv("S3_ACCESS_KEY"),
    # #         "s3_secret_key": os.getenv("S3_SECRET_KEY"),
    # #         "s3_bucket": os.getenv("S3_BUCKET"),
    # #         "clickhouse_host": os.getenv("CLICKHOUSE_HOST"),
    # #         "clickhouse_port": os.getenv("CLICKHOUSE_PORT"),
    # #         "clickhouse_user": os.getenv("CLICKHOUSE_USER"),
    # #         "clickhouse_password": os.getenv("CLICKHOUSE_PASSWORD"),
    # #         "clickhouse_database": os.getenv("CLICKHOUSE_DATABASE"),
    # #         "clickhouse_verify": os.getenv("CLICKHOUSE_ENFORCE_SSL", "False"),
    # #         "repository": self.repository,
    # #         "notes": self.notes
    # #     }

    # #     try:
    # #         # Get list of files
    # #         if os.path.isfile(self.path):
    # #             files = [self.path] if not self.path.endswith('.txt') else [
    # #                 line.strip() for line in open(self.path) 
    # #                 if line.strip() and not os.path.basename(line.strip()).startswith('.')
    # #             ]
    # #         elif os.path.isdir(self.path):
    # #             files = [
    # #                 os.path.join(root, file)
    # #                 for root, _, files in os.walk(self.path)
    # #                 for file in files
    # #                 if not file.startswith('.')
    # #             ]
    # #         else:
    # #             print(f"[ERR] Invalid path: {self.path}")
    # #             return

    # #         total_files = len(files)
    # #         print(f"Found {total_files} files to process")

    # #         with Pool(processes=max(1, multiprocessing.cpu_count() - 1)) as pool:
    # #             results = pool.map(
    # #                 process_single_file,
    # #                 [(f, i + 1, total_files, config, self.log_file) for i, f in enumerate(files)]
    # #             )

    # #         # Update statistics
    # #         for result in results:
    # #             if result is not None:
    # #                 self.total_results[result] += 1

    # #         # Generate summary
    # #         end_time = time.time()
    # #         elapsed_time = end_time - start_time
    # #         elapsed_time_pretty = time.strftime("%H:%M:%S", time.gmtime(elapsed_time))

    # #         summary = (
    # #             f"\n\nUpload finished for {self.path}"
    # #             f"\nTime required: {elapsed_time_pretty}"
    # #             f"\nResults:"
    # #             f"\n- Total processed: {sum(self.total_results.values())}"
    # #             f"\n- Successfully uploaded: {self.total_results[UploadResult.CORRECTLY]}"
    # #             f"\n- Skipped (already exists): {self.total_results[UploadResult.SKIPPED]}"
    # #             f"\n- Failed: {self.total_results[UploadResult.FAILED]}\n"
    # #         )

    # #         with open(self.log_file, "a") as f:
    # #             f.write(summary)

    # #         print(summary)

    # #     except Exception as e:
    # #         print(f"[ERR] Error in upload process: {str(e)}")

    # def upload(self):
    #     """Main upload function with multiprocessing support"""
    #     start_time = time.time()
    #     BATCH_SIZE = 1000  # Process files in batches of 1000

    #     config = {
    #         "s3_endpoint": os.getenv("S3_ENDPOINT"),
    #         "s3_access_key": os.getenv("S3_ACCESS_KEY"),
    #         "s3_secret_key": os.getenv("S3_SECRET_KEY"),
    #         "s3_bucket": os.getenv("S3_BUCKET"),
    #         "clickhouse_host": os.getenv("CLICKHOUSE_HOST"),
    #         "clickhouse_port": os.getenv("CLICKHOUSE_PORT"),
    #         "clickhouse_user": os.getenv("CLICKHOUSE_USER"),
    #         "clickhouse_password": os.getenv("CLICKHOUSE_PASSWORD"),
    #         "clickhouse_database": os.getenv("CLICKHOUSE_DATABASE"),
    #         "clickhouse_verify": os.getenv("CLICKHOUSE_ENFORCE_SSL", "False"),
    #         "repository": self.repository,
    #         "notes": self.notes
    #     }

    #     try:
    #         # Get list of files
    #         if os.path.isfile(self.path):
    #             files = [self.path] if not self.path.endswith('.txt') else [
    #                 line.strip() for line in open(self.path) 
    #                 if line.strip() and not os.path.basename(line.strip()).startswith('.')
    #             ]
    #         elif os.path.isdir(self.path):
    #             files = [
    #                 os.path.join(root, file)
    #                 for root, _, files in os.walk(self.path)
    #                 for file in files
    #                 if not file.startswith('.')
    #             ]
    #         else:
    #             print(f"[ERR] Invalid path: {self.path}")
    #             return

    #         total_files = len(files)
    #         print(f"Found {total_files} files to process")

    #         # Process files in batches
    #         for i in range(0, total_files, BATCH_SIZE):
    #             batch_files = files[i:i + BATCH_SIZE]
    #             batch_start = i + 1
    #             batch_end = min(i + BATCH_SIZE, total_files)
    #             print(f"\nProcessing batch {batch_start}-{batch_end} of {total_files}")
                
    #             with Pool(processes=max(1, multiprocessing.cpu_count() - 1)) as pool:
    #                 batch_results = pool.map(
    #                     process_single_file,
    #                     [(f, j + 1, total_files, config, self.log_file) 
    #                         for j, f in enumerate(batch_files, start=i)]
    #                 )
                    
    #                 # Update statistics for this batch
    #                 for result in batch_results:
    #                     if result is not None:
    #                         self.total_results[result] += 1

    #                 # Print intermediate summary
    #                 print(f"Batch {batch_start}-{batch_end} completed:")
    #                 print(f"- Successfully uploaded: {sum(1 for r in batch_results if r == UploadResult.CORRECTLY)}")
    #                 print(f"- Skipped (exists): {sum(1 for r in batch_results if r == UploadResult.SKIPPED)}")
    #                 print(f"- Failed: {sum(1 for r in batch_results if r == UploadResult.FAILED)}")

    #         # Generate final summary
    #         end_time = time.time()
    #         elapsed_time = end_time - start_time
    #         elapsed_time_pretty = time.strftime("%H:%M:%S", time.gmtime(elapsed_time))

    #         summary = (
    #             f"\n\nUpload finished for {self.path}"
    #             f"\nTime required: {elapsed_time_pretty}"
    #             f"\nResults:"
    #             f"\n- Total processed: {sum(self.total_results.values())}"
    #             f"\n- Successfully uploaded: {self.total_results[UploadResult.CORRECTLY]}"
    #             f"\n- Skipped (already exists): {self.total_results[UploadResult.SKIPPED]}"
    #             f"\n- Failed: {self.total_results[UploadResult.FAILED]}\n"
    #         )

    #         with open(self.log_file, "a") as f:
    #             f.write(summary)

    #         print(summary)

    #     except Exception as e:
    #         print(f"[ERR] Error in upload process: {str(e)}")

    def upload(self):
        """Main upload function with multiprocessing support"""
        start_time = time.time()
        BATCH_SIZE = 1000  # Process files in batches of 1000

        config = {
            "s3_endpoint": os.getenv("S3_ENDPOINT"),
            "s3_access_key": os.getenv("S3_ACCESS_KEY"),
            "s3_secret_key": os.getenv("S3_SECRET_KEY"),
            "s3_bucket": os.getenv("S3_BUCKET"),
            "clickhouse_host": os.getenv("CLICKHOUSE_HOST"),
            "clickhouse_port": os.getenv("CLICKHOUSE_PORT"),
            "clickhouse_user": os.getenv("CLICKHOUSE_USER"),
            "clickhouse_password": os.getenv("CLICKHOUSE_PASSWORD"),
            "clickhouse_database": os.getenv("CLICKHOUSE_DATABASE"),
            "clickhouse_verify": os.getenv("CLICKHOUSE_ENFORCE_SSL", "False"),
            "repository": self.repository,
            "notes": self.notes
        }

        try:
            # Get all existing filenames for this repository
            query = f"SELECT filename FROM samples_catalog WHERE repository = '{self.repository}'"
            # existing_files = set()
            # for row in self.ch_client.query(query).result_rows:
            #     filename = os.path.splitext(row[0])[0]  # Strip extension
            #     existing_files.add(filename)
            existing_files = set(row[0] for row in self.ch_client.query(query).result_rows)
            print(f"Found {len(existing_files)} existing files in repository")

            # Get list of files to process
            if os.path.isfile(self.path):
                files = [self.path] if not self.path.endswith('.txt') else [
                    line.strip() for line in open(self.path) 
                    if line.strip() and not os.path.basename(line.strip()).startswith('.')
                ]
            elif os.path.isdir(self.path):
                files = [
                    os.path.join(root, file)
                    for root, _, files in os.walk(self.path)
                    for file in files
                    if not file.startswith('.')
                ]
            else:
                print(f"[ERR] Invalid path: {self.path}")
                return

            # Filter out files that are already in the repository
            files_to_process = []
            for f in files:
                basename = os.path.splitext(os.path.basename(f))[0]
                if basename not in existing_files:
                    files_to_process.append(f)

            total_files = len(files_to_process)
            skipped_files = len(files) - total_files
            print(f"Found {total_files} new files to process (skipped {skipped_files} existing files)")

            if total_files == 0:
                print("No new files to process")
                return

            # Process files in batches
            for i in range(0, total_files, BATCH_SIZE):
                batch_files = files_to_process[i:i + BATCH_SIZE]
                batch_start = i + 1
                batch_end = min(i + BATCH_SIZE, total_files)
                print(f"\nProcessing batch {batch_start}-{batch_end} of {total_files}")
                
                with Pool(processes=max(1, multiprocessing.cpu_count() - 1)) as pool:
                    batch_results = pool.map(
                        process_single_file,
                        [(f, j + 1, total_files, config, self.log_file) 
                        for j, f in enumerate(batch_files, start=i)]
                    )
                    
                    # Update statistics for this batch
                    for result in batch_results:
                        if result is not None:
                            self.total_results[result] += 1

                    # Print intermediate summary
                    print(f"Batch {batch_start}-{batch_end} completed:")
                    print(f"- Successfully uploaded: {sum(1 for r in batch_results if r == UploadResult.CORRECTLY)}")
                    print(f"- Skipped (exists): {sum(1 for r in batch_results if r == UploadResult.SKIPPED)}")
                    print(f"- Failed: {sum(1 for r in batch_results if r == UploadResult.FAILED)}")

            # Generate final summary
            end_time = time.time()
            elapsed_time = end_time - start_time
            elapsed_time_pretty = time.strftime("%H:%M:%S", time.gmtime(elapsed_time))

            summary = (
                f"\n\nUpload finished for {self.path}"
                f"\nTime required: {elapsed_time_pretty}"
                f"\nResults:"
                f"\n- Files already in repository: {skipped_files}"
                f"\n- New files processed: {total_files}"
                f"\n- Successfully uploaded: {self.total_results[UploadResult.CORRECTLY]}"
                f"\n- Skipped (already exists): {self.total_results[UploadResult.SKIPPED]}"
                f"\n- Failed: {self.total_results[UploadResult.FAILED]}\n"
            )

            with open(self.log_file, "a") as f:
                f.write(summary)

            print(summary)

        except Exception as e:
            print(f"[ERR] Error in upload process: {str(e)}")
            import traceback
            print(traceback.format_exc())

if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(description='Upload malware samples to S3 and catalog them.')
    parser.add_argument('path', help='Path to file or directory to process')
    parser.add_argument('repository', help='Repository name')
    parser.add_argument('--notes', help='Optional notes to add to all files', default=None)
    
    args = parser.parse_args()
    
    uploader = S3Uploader(args.path, args.repository, args.notes)
    uploader.upload()