使用JAVA api读取HDFS文件乱码踩坑

想写一个读取HFDS上的部分文件数据做预览的接口,根据网上的博客实现后,发现有时读取信息会出现乱码,例如读取一个csv时,字符串之间被逗号分割

  • 英文字符串aaa,能正常显示

  • 中文字符串“你好”,能正常显示

  • 中英混合字符串如“aaa你好”,出现乱码

查阅了众多博客,解决方案大概都是:使用xxx字符集解码。抱着不信的想法,我依次尝试,果然没用。

解决思路

因为HDFS支持6种字符集编码,每个本地文件编码方式又是极可能不一样的,我们上传本地文件的时候其实就是把文件编码成字节流上传到文件系统存储。那么在GET文件数据时,面对不同文件、不同字符集编码的字节流,肯定不是一种固定字符集解码就能正确解码的吧。

那么解决方案其实有两种

  • 固定HDFS的编解码字符集。比如我选用UTF-8,那么在上传文件时统一编码,即把不同文件的字节流都转化为UTF-8编码再进行存储。这样的话在获取文件数据的时候,采用UTF-8字符集解码就没什么问题了。但这样做的话仍然会在转码部分存在诸多问题,且不好实现。

  • 动态解码。根据文件的编码字符集选用对应的字符集对解码,这样的话并不会对文件的原生字符流进行改动,基本不会乱码。

我选用动态解码的思路后,其难点在于如何判断使用哪种字符集解码。

好在看到了一篇博客

https://blog.csdn.net/smallnetvisitor/article/details/84682867

Google提供了检测字节流编码方式的包。那么方案就很明了了,先读一些文件字节流,用工具检测编码方式,再对应进行解码即可。

具体代码

pom

  1. <dependency>
  2. <groupId>net.sourceforge.jchardet</groupId>
  3. <artifactId>jchardet</artifactId>
  4. <version>1.0</version>
  5. </dependency>

从HDFS读取部分文件做预览的逻辑

  1. // 获取文件的部分数据做预览
  2. public List<String> getFileDataWithLimitLines(String filePath, Integer limit) {
  3. FSDataInputStream fileStream = openFile(filePath);
  4. return readFileWithLimit(fileStream, limit);
  5. }
  6. // 获取文件的数据流
  7. private FSDataInputStream openFile(String filePath) {
  8. FSDataInputStream fileStream = null;
  9. try {
  10. fileStream = fs.open(new Path(getHdfsPath(filePath)));
  11. } catch (IOException e) {
  12. logger.error("fail to open file:{}", filePath, e);
  13. }
  14. return fileStream;
  15. }
  16. // 读取最多limit行文件数据
  17. private List<String> readFileWithLimit(FSDataInputStream fileStream, Integer limit) {
  18. byte[] bytes = readByteStream(fileStream);
  19. String data = decodeByteStream(bytes);
  20. if (data == null) {
  21. return null;
  22. }
  23. List<String> rows = Arrays.asList(data.split("\\r\\n"));
  24. return rows.stream().filter(StringUtils::isNotEmpty)
  25. .limit(limit)
  26. .collect(Collectors.toList());
  27. }
  28. // 从文件数据流中读取字节流
  29. private byte[] readByteStream(FSDataInputStream fileStream) {
  30. byte[] bytes = new byte[1024*30];
  31. int len;
  32. ByteArrayOutputStream stream = new ByteArrayOutputStream();
  33. try {
  34. while ((len = fileStream.read(bytes)) != -1) {
  35. stream.write(bytes, 0, len);
  36. }
  37. } catch (IOException e) {
  38. logger.error("read file bytes stream failed.", e);
  39. return null;
  40. }
  41. return stream.toByteArray();
  42. }
  43. // 解码字节流
  44. private String decodeByteStream(byte[] bytes) {
  45. if (bytes == null) {
  46. return null;
  47. }
  48. String encoding = guessEncoding(bytes);
  49. String data = null;
  50. try {
  51. data = new String(bytes, encoding);
  52. } catch (Exception e) {
  53. logger.error("decode byte stream failed.", e);
  54. }
  55. return data;
  56. }
  57. // 根据Google的工具判别编码
  58. private String guessEncoding(byte[] bytes) {
  59. UniversalDetector detector = new UniversalDetector(null);
  60. detector.handleData(bytes, 0, bytes.length);
  61. detector.dataEnd();
  62. String encoding = detector.getDetectedCharset();
  63. detector.reset();
  64. if (StringUtils.isEmpty(encoding)) {
  65. encoding = "UTF-8";
  66. }
  67. return encoding;
  68. }

使用JAVA API读取HDFS的文件数据出现乱码的解决方案的更多相关文章

  1. Java API 读取HDFS的单文件

    HDFS上的单文件: -bash-3.2$ hadoop fs -ls /user/pms/ouyangyewei/data/input/combineorder/repeat_rec_categor ...

  2. java Api 读取HDFS文件内容

    package dao; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.*; import java ...

  3. Spark:java api读取hdfs目录下多个文件

    需求: 由于一个大文件,在spark中加载性能比较差.于是把一个大文件拆分为多个小文件后上传到hdfs,然而在spark2.2下如何加载某个目录下多个文件呢? public class SparkJo ...

  4. 用java api读取HDFS文件

    import java.io.IOException; import java.io.InputStream; import java.security.PrivilegedExceptionActi ...

  5. JAVA API 实现hdfs文件操作

    java api 实现hdfs 文件操作会出现错误提示: Permission denied: user=hp, access=WRITE, inode="/":hdfs:supe ...

  6. 分享非常有用的Java程序(关键代码)(八)---Java InputStream读取网络响应Response数据的方法!(重要)

    原文:分享非常有用的Java程序(关键代码)(八)---Java InputStream读取网络响应Response数据的方法!(重要) Java InputStream读取数据问题 ======== ...

  7. 使用Java API操作HDFS文件系统

    使用Junit封装HFDS import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.*; import org ...

  8. 使用Java Api 操作HDFS

    如题 我就是一个标题党  就是使用JavaApi操作HDFS,使用的是MAVEN,操作的环境是Linux 首先要配置好Maven环境,我使用的是已经有的仓库,如果你下载的jar包 速度慢,可以改变Ma ...

  9. C#选择多个文件并读取多个文件数据

    原文:C#选择多个文件并读取多个文件数据 版权声明:本文为博主原创文章,转载请附上链接地址. https://blog.csdn.net/ld15102891672/article/details/8 ...

随机推荐

  1. ORA-00018: maximum number of sessions exceeded 超出最大会话数

    ORA-00018: maximum number of sessions exceededORA-00018: 超出最大会话数 Cause:       All session state obje ...

  2. C# Socket TCP发送图片与接收图片

    如果需要查看更多文章,请微信搜索公众号 csharp编程大全,需要进C#交流群群请加微信z438679770,备注进群, 我邀请你进群! ! ! --------------------------- ...

  3. Angluar2 项目搭建

    一 使用 Angular CLI 官方脚手架 1.安装 cli npm install -g @angular/cli 2.创建工作空间和初始应用 ng new my-app 二 tsLint 代码格 ...

  4. Word+Excel 问题及解决

    [Word] 快捷操作 (1)每个字后面都有换行符的处理办法: 替换:∧p -> 空格 (2)隐藏Word文档中的换行符: word选项 -> 显示 -> 段落标记 [Excel]

  5. docker 和 k8s 调研总结

    一. docker简介 环境配置 软件开发最大的麻烦事之一,就是环境配置.用户计算机的环境都不相同,你怎么知道自家的软件,能在那些机器跑起来? 用户必须保证两件事:操作系统的设置,各种库和组件的安装. ...

  6. k8s集群添加新得node节点

    服务端操作: 方法一: 获取master的join token kubeadm token create --print-join-command 重新加入节点 kubeadm join 192.16 ...

  7. 习题3-4 周期串(Periodic Strings, UVa455)

    #include<stdio.h> #include<string.h> char s[100]; int main() { int T; scanf("%d&quo ...

  8. 【UR #2】猪猪侠再战括号序列

    UOJ小清新题表 题目摘要 UOJ链接 有一个由 \(n\) 个左括号 "(" 和 \(n\) 个右括号 ")" 组成的序列.每次操作时可以选定两个数 \(l, ...

  9. 【原创】xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程

    版权声明:本文为本文为博主原创文章,转载请注明出处.如有问题,欢迎指正.博客地址:https://www.cnblogs.com/wsg1100/ 1.概述 上篇文章xenomai内核解析--实时IP ...

  10. spring注解@Transactional 和乐观锁,悲观锁并发生成有序编号问题

      需求:系统中有一个自增的合同编号,在满足并发情况下,生成的合同编号是自增的. 测试工具:Apache Jmeter 实现方法: 创建一个数据库表.编号最大值记录表 表结构类似 CREATE TAB ...