基于虹软SDK的Android人脸、性别、年龄识别功能及Java后端整合统一人脸库

 

虹软人脸识别SDK1.2版本是免费的,但是官方提供的Demo是离线版本的,人脸数据保存在手机上,换一部手机就无法识别。本文基于其进行Android版本的人脸识别功能、性别识别功能、年龄识别功能开发,并在Java后端建立人脸库,统一管理人脸识别数据,做到A手机上注册,在B手机上也能识别的功能。

一、下载官方提供的Demo

地址为 https://github.com/asdfqwrasdf/ArcFaceDemo,下载完后解压,使用Android Studio打开

二、下面是注册各种ID值和下载SDK,实现官方Demo的离线人脸识别功能:

第二步:去虹软人脸识别平台:https://ai.arcsoft.com.cn/  进行注册、登录,然后进行个人实名认证。不实名认证的话有的功能会被限制使用

第三步:如下新建应用:

第四步:选择功能为人脸识别:

第五步:填写信息:

第六步:注意下图的的APP ID和SDK KEY,后面要复制到Demo中去

第七步:下载上图中的SDK,下载下来一共有五个压缩包,每一个都要解压,并把每一个压缩包中的libs中的东西复制到Demo项目中的libs中去,其中五个压缩包中的armeabi下的libmpbase.so都是一样的,复制时选择覆盖,同理五个包中的armeabi-v7a下的libmpbase.so也是一样的,复制时选择覆盖:

五个压缩包如下:

复制到Demo项目中后,项目如下:

第八步、将官方平台上的各种key和id复制到FaceDB类中,对应如下:

   /**
     * APP ID
     */
    public static String appid = "xxx";
    /**
     * 人脸追踪(FT)
     */
    public static String ft_key = "xxx";
    /**
     * 人脸检测(FD)
     */
    public static String fd_key = "xxx";
    /**
     * 人脸识别(FR)
     */
    public static String fr_key = "xxx";
    /**
     * 年龄识别(Age)
     */
    public static String age_key = "xxx";
    /**
     * 性别识别(Gender)
     */
    public static String gender_key

运行APP,先点击注册人脸数据,并为注册人脸图片命名,然后进行识别,就能看到置信度分数(即匹配程度分数)。当这个分数大于0.6时(这个值可以自己调整),说明识别成功

三、改造官方Demo,实现人脸数据上传到服务器并从服务器同步

要实现这个功能,主要改造的是Demo中的FaceDB类,因为人脸注册的时候,数据是通过这个类保存在SD卡上的,人脸识别的时候,数据也是通过这个类从SD卡加载的,改造后如下:注意复制官网提供的各种Key和ID值到本类中;另外注意人脸数据上传下载的两个链接为Java后端提供的链接

/**
 * 人脸识别数据操作相关
 */
public class FaceDB
{
    private final String TAG = this.getClass().toString();

    //以下几个id和key是从虹软人脸识别平台(https://ai.arcsoft.com.cn/)申请的。使用的是V1.2版本
    /**
     * APP ID
     */
    public static String appid = "xxx";
    /**
     * 人脸追踪(FT)
     */
    public static String ft_key = "xxx";
    /**
     * 人脸检测(FD)
     */
    public static String fd_key = "xxx";
    /**
     * 人脸识别(FR)
     */
    public static String fr_key = "xxx";
    /**
     * 年龄识别(Age)
     */
    public static String age_key = "xxx";
    /**
     * 性别识别(Gender)
     */
    public static String gender_key = "xxx";

    /**
     * 人脸数据存储在本地SD卡的路径
     */
    String mDBPath;
    /**
     * 人脸注册数据集合:FaceRegist是一个DataBean,其中包含注册图片的名称和保存人脸特征信息的AFR_FSDKFace的Map集合
     */
    List<FaceRegist> mRegister;
    /**
     * 这个类具体实现了人脸识别的功能
     */
    AFR_FSDKEngine mFREngine;
    /**
     * 这个类用来保存版本信息
     */
    AFR_FSDKVersion mFRVersion;
    /**
     * 是否更新用户数据
     */
    boolean mUpgrade;

    /**
     * 人脸注册信息DataBean
     */
    class FaceRegist
    {
        /**
         * 注册的图片名称
         */
        String mName;
        /**
         * 人脸特征信息集合。第一个泛型是图片链接,第二个泛型AFR_FSDKFace: 这个类用来保存人脸特征信息
         */
        Map<String, AFR_FSDKFace> mFaceList;

        /**
         * 构造方法中设置图片名称
         *
         * @param name : 图片名称
         */
        public FaceRegist(String name)
        {
            mName = name;
            mFaceList = new LinkedHashMap<>();
        }
    }

    /**
     * 人脸数据上传链接。需要上传face.txt和xxx.data
     */
    private String uploadUrl = "http://localhost:10086/upload";
    /**
     * 人脸数据下载链接。需要下载face.txt和xxx.data
     */
    private String downloadUrl = "http://localhost:10086/download?fileName=";

    /***
     * 文件上传下载工具
     */
    private FaceFileUtils faceFileUtils;

    /**
     * 本类构造方法:其中初始化FaceDB相关数据
     *
     * @param path :数据存储路径
     */
    public FaceDB(String path)
    {
        mDBPath = path;
        mRegister = new ArrayList<>();
        mFRVersion = new AFR_FSDKVersion();
        mUpgrade = false;
        mFREngine = new AFR_FSDKEngine();
        /**AFR_FSDKError: 这个类用来保存错误信息*/
        AFR_FSDKError error = mFREngine.AFR_FSDK_InitialEngine(FaceDB.appid, FaceDB.fr_key);
        if (error.getCode() != AFR_FSDKError.MOK) //MOK: 成功
        {//如果AFR_FSDKEngine初始化失败
            Log.e(TAG, "AFR_FSDKEngine初始化失败! 错误码为:" + error.getCode());
        } else
        {//如果AFR_FSDKEngine初始化成功
            mFREngine.AFR_FSDK_GetVersion(mFRVersion);
            Log.d(TAG, "AFR_FSDKEngine初始化成功,版本号为:" + mFRVersion.toString());
        }

        //创建文件上传下载工具实例
        faceFileUtils = new FaceFileUtils(mDBPath, uploadUrl, downloadUrl);

        //程序启动时,先同步下载服务器上的face.txt和所有的xxx.data数据
        faceFileUtils.downloadFile();
    }

    /**
     * 销毁AFR_FSDKEngine
     */
    public void destroy()
    {
        if (mFREngine != null)
        {
            mFREngine.AFR_FSDK_UninitialEngine();
        }
    }

    /**
     * 保存版本信息及特征级别到数据存储路径下的face.txt
     *
     * @return :返回值为是否保存成功
     */
    private boolean saveInfo()
    {
        try
        {
            FileOutputStream fs = new FileOutputStream(mDBPath + "/face.txt");
            byte[] data = (mFRVersion.toString() + "," + mFRVersion.getFeatureLevel() + " ").getBytes();
            fs.write(data);
            fs.close();

            Log.d(TAG, "保存在face.txt中的信息为:" + mFRVersion.toString() + "," + mFRVersion.getFeatureLevel());

            return true;
        } catch (FileNotFoundException e)
        {
            e.printStackTrace();
        } catch (IOException e)
        {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 从face.txt中加载图片名称
     *
     * @return :返回值为是否加载成功
     */
    private boolean loadInfo()
    {
        if (!mRegister.isEmpty())
        {//如果不为空
            return false;
        }

        try
        {
            FileInputStream fs = new FileInputStream(mDBPath + "/face.txt");
            byte[] buf = new byte[1024];
            int length = 0;
            String version_saved = null;
            while ((length = fs.read(buf)) != -1)
            {
                version_saved = new String(buf, 0, length);
            }

            System.out.println("读取的数据为:" + version_saved);

            if (version_saved.equals(mFRVersion.toString() + "," + mFRVersion.getFeatureLevel() + " "))
            {
                mUpgrade = true;
            }

            //加载所有已经注册在face.txt中的名称.
            if (version_saved != null)
            {
                String[] names = version_saved.split(" ");

                for (int i = 1; i < names.length; i++) //从第二个开始读取
                {
                    if (new File(mDBPath + "/" + names[i] + ".data").exists())
                    {
                        //这里拿到的是所有的注册图片的名称
                        mRegister.add(new FaceRegist(new String(names[i])));
                    }
                }
            }
            fs.close();
            return true;
        } catch (FileNotFoundException e)
        {
            e.printStackTrace();
        } catch (IOException e)
        {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 删除文件夹下的文件
     */
    public static void delFiles(String filePath)
    {
        File file = new File(filePath);
        if (!file.exists())
        {
            return;
        }
        String[] list = file.list();
        File temp = null;
        String path = null;
        for (String item : list)
        {
            path = filePath + File.separator + item;
            temp = new File(path);
            if (temp.isFile())
            {
                temp.delete();
                continue;
            }
            if (temp.isDirectory())
            {
                delFiles(path);
                new File(path).delete();
                continue;
            }
        }
    }

    /**
     * 加载人脸特征数据。在PermissionAcitivity的130行调用此方法
     *
     * @return :返回值为是否加载成功
     */
    public boolean loadFaces()
    {
        if (loadInfo())
        {
            try
            {
                for (FaceRegist face : mRegister)
                {
                    Log.d(TAG, "加载名称为:" + face.mName + "的图片的特征数据");
                    FileInputStream fs = new FileInputStream(mDBPath + "/" + face.mName + ".data");
                    ExtInputStream bos = new ExtInputStream(fs);
                    /**这个类用来保存人脸特征信息*/
                    AFR_FSDKFace afr = null;
                    do
                    { //第一次先从上到下走循环体,再判断while条件
                        if (afr != null)
                        {
                            if (mUpgrade)
                            {
                                //upgrade data.
                            }
                            //keyFile为/storage/emulated/0/Android/data/com.arcsoft.sdk_demo/cache/3886197395912.jpg
                            //读取存入xx.data中的图片路径值
                            String keyFile = bos.readString();
                            face.mFaceList.put(keyFile, afr);
                        }
                        afr = new AFR_FSDKFace();
                    } while (bos.readBytes(afr.getFeatureData())); //经过这个条件判断,就把.data中的数据读取到了AFR_FSDKFace中

                    bos.close();
                    fs.close();
                    Log.d(TAG, "加载到的图片数据有:" + face.mFaceList.size() + "条");
                }
                return true;
            } catch (FileNotFoundException e)
            {
                e.printStackTrace();
            } catch (IOException e)
            {
                e.printStackTrace();
            }
        }
        return false;
    }

    /**
     * 添加人脸特征数据
     *
     * @param name     : 图片名称
     * @param face     :人脸特征信息
     * @param faceicon : Bitmap图片
     */
    public void addFace(String name, AFR_FSDKFace face, Bitmap faceicon)
    {
        try
        {
            //1、保存人脸图片
            String keyPath = mDBPath + "/" + System.nanoTime() + ".jpg";//设置图片链接。System.nanoTime():返回的是纳秒
            File keyFile = new File(keyPath);
            OutputStream stream = new FileOutputStream(keyFile);

            /**compress方法:第一个参数是压缩格式,第二个参数是压缩质量,第三个参数是把压缩图片存到输出流指定的路径去*/
            if (faceicon.compress(Bitmap.CompressFormat.JPEG, 80, stream))
            {//调用compress方法的时候,其实已经执行了输出流保存的操作
                Log.d(TAG, "图片保存成功!");
            }
            stream.close();

            //2、检查是否已经注册
            boolean add = true; //默认未注册
            for (FaceRegist frface : mRegister)
            {
                if (frface.mName.equals(name))
                {
                    frface.mFaceList.put(keyPath, face);
                    add = false; //设置为已注册
                    break;
                }
            }

            if (add)
            { //如果没有注册
                FaceRegist frface = new FaceRegist(name);
                frface.mFaceList.put(keyPath, face);
                mRegister.add(frface);
            }

            //3、保存新的图片名称和特征数据
            if (saveInfo())
            {
                //在face.txt中继续添加图片名称
                FileOutputStream fs = new FileOutputStream(mDBPath + "/face.txt", true);//true表示追加

                for (FaceRegist frface : mRegister)
                {
                    fs.write((frface.mName + " ").getBytes());
                }
                fs.close();

                //追加成功后上传face.txt文件
                faceFileUtils.uploadFile("face.txt");

                //保存新的人脸特征数据
                fs = new FileOutputStream(mDBPath + "/" + name + ".data", true);//true表示追加
                ExtOutputStream bos = new ExtOutputStream(fs);
                bos.writeBytes(face.getFeatureData());//保存人脸特征数据
                bos.writeString(keyPath);//保存图片路径值到xx.data中
                bos.close();
                fs.close();

                //上传.data文件
                faceFileUtils.uploadFile(name + ".data");

                //等到上传完成再删除
                SystemClock.sleep(500);

                //上传本目录下的所有文件。注意当前打开程序注册的这一次数据已经加载到集合mRegister中,即使删除所有face.txt文件和xxx.data文件,也不会影响当前注册这一次
                delFiles(mDBPath);
            }
        } catch (FileNotFoundException e)
        {
            e.printStackTrace();
        } catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    /**
     * 删除人脸数据
     *
     * @param name :图片名称
     * @return :是否删除成功
     */
    public boolean delete(String name)
    {
        try
        {
            //检查是否已经注册
            boolean find = false;//删除成功设置为true
            for (FaceRegist frface : mRegister)
            {
                if (frface.mName.equals(name))
                {
                    File delfile = new File(mDBPath + "/" + name + ".data");
                    if (delfile.exists())
                    {
                        delfile.delete();
                    }
                    mRegister.remove(frface);
                    find = true;//删除成功设置为true
                    break;
                }
            }

            if (find)
            {//如果删除成功
                if (saveInfo())
                {
                    //更新face.txt中的数据
                    FileOutputStream fs = new FileOutputStream(mDBPath + "/face.txt", true);//true表示追加
                    for (FaceRegist frface : mRegister)
                    {
                        fs.write((frface.mName + " ").getBytes());
                    }
                    fs.close();
                }
            }
            return find;
        } catch (FileNotFoundException e)
        {
            e.printStackTrace();
        } catch (IOException e)
        {
            e.printStackTrace();
        }
        return false;
    }

    public boolean upgrade()//未用
    {
        return false;
    }
}

其中使用的三个基于OkHttp的文件上传下载工具类如下:

/***
 *脸部数据上传下载工具类
 */
public class FaceFileUtils
{
    private String mDBPath;
    private String uploadUrl;
    private String downloadUrl;

    public FaceFileUtils(String mDBPath, String uploadUrl, String downloadUrl)
    {
        this.mDBPath = mDBPath;
        this.uploadUrl = uploadUrl;
        this.downloadUrl = downloadUrl;
    }

    /***
     *上传文件到服务器
     *
     * @param name :上传文件名,包括后缀名
     */
    public void uploadFile(String name)
    {
        //以流的形式上传文件
        MediaType type = MediaType.parse("application/octet-stream");
        //指向文件
        File file = new File(mDBPath + "/" + name);
        //创建请求体
        RequestBody fileBody = RequestBody.create(type, file);

        //构建请求体和请求参数。file是后台接口请求参数名
        RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM).addFormDataPart("file", name, fileBody).build();

        /***
         * 上传文件
         */
        UploadFile.sendOkHttpRequestPost(uploadUrl, requestBody, new Callback()
        {
            @Override
            public void onFailure(Call call, IOException e)
            {
                toastShow("数据上传失败!");

                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException
            {
                toastShow("数据上传成功!");
            }
        });
    }

    /***
     *从服务器下载脸部数据文件
     */
    public void downloadFile()
    {
        //先下载face.txt
        download("face.txt", mDBPath);

        /**
         * 再获取face.txt文件的内容
         */
        UploadFile.sendOkHttpRequestGET(downloadUrl + "face.txt", new Callback()
        {
            @Override
            public void onFailure(Call call, IOException e)
            {
                toastShow("脸部数据获取失败!");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException
            {
                //这里能拿到face.txt中的文本数据:内容为:1.2.0.42,0 qwer 123
                final String responsedata = response.body().string();

                //按照空格切开
                String[] names = responsedata.split(" ");

                for (int i = 1; i < names.length; i++) //从第二个开始
                {
                    download(names[i] + ".data", mDBPath);
                }
            }
        });
    }

    /**
     * 下载文件
     *
     * @param fileName :文件名,包括后缀
     * @param savePath : 下载文件保存路径
     */
    private void download(String fileName, String savePath)
    {
        DownloadFile.get().download(downloadUrl + fileName, savePath, new DownloadFile.OnDownloadListener()
        {
            @Override
            public void onDownloadSuccess()
            {
                toastShow("数据同步成功!");
            }

            @Override
            public void onDownloading(int progress)
            {
                toastShow("进度:" + progress);
            }

            @Override
            public void onDownloadFailed()
            {
                toastShow("数据同步失败!");
            }
        });
    }

    public void toastShow(final String msg)
    {
        Log.d("文件日志", msg);
    }
}
/***
 *下载文件工具类
 */
public class DownloadFile
{
    private static DownloadFile downloadFile;
    private final OkHttpClient okHttpClient;

    /***
     * 单例模式获取本类对象
     * @return
     */
    public static DownloadFile get()
    {
        if (downloadFile == null)
        {
            downloadFile = new DownloadFile();
        }
        return downloadFile;
    }

    private DownloadFile()
    {
        okHttpClient = new OkHttpClient();
    }

    /**
     * 下载文件
     *
     * @param url      下载链接
     * @param saveDir  储存下载文件的SDCard目录
     * @param listener 下载监听
     */
    public void download(final String url, final String saveDir, final OnDownloadListener listener)
    {
        Request request = new Request.Builder().url(url).build();
        okHttpClient.newCall(request).enqueue(new Callback()
        {
            @Override
            public void onFailure(okhttp3.Call call, IOException e)
            {
                // 下载失败
                listener.onDownloadFailed();
            }

            @Override
            public void onResponse(okhttp3.Call call, Response response) throws IOException
            {
                InputStream is = null;
                byte[] buf = new byte[2048];
                int len = 0;
                FileOutputStream fos = null;
                try
                {
                    is = response.body().byteStream();
                    long total = response.body().contentLength();
                    File file = new File(saveDir, getNameFromUrl(url));
                    fos = new FileOutputStream(file);
                    long sum = 0;
                    while ((len = is.read(buf)) != -1)
                    {
                        fos.write(buf, 0, len);
                        sum += len;
                        int progress = (int) (sum * 1.0f / total * 100);
                        // 下载中
                        listener.onDownloading(progress);
                    }
                    fos.flush();
                    // 下载完成
                    listener.onDownloadSuccess();
                } catch (Exception e)
                {
                    listener.onDownloadFailed();
                } finally
                {
                    try
                    {
                        if (is != null) is.close();
                    } catch (IOException e)
                    {
                    }
                    try
                    {
                        if (fos != null) fos.close();
                    } catch (IOException e)
                    {
                    }
                }
            }
        });
    }
    /**
     * 从下载连接中解析出文件名。以=为界
     *
     * @param url :下载连接
     * @return 文件名
     */
    private String getNameFromUrl(String url)
    {
        return url.substring(url.lastIndexOf("=") + 1);
    }

    public interface OnDownloadListener
    {
        /**
         * 下载成功
         */
        void onDownloadSuccess();

        /**
         * 下载进度
         *
         * @param progress :进度值
         */
        void onDownloading(int progress);

        /**
         * 下载失败
         */
        void onDownloadFailed();
    }
}
/***
 *上传文件工具类
 */
public class UploadFile
{
    /**
     * POST方式访问网络
     *
     * @param address     :请求地址
     * @param requestBody :请求体
     * @param callback    :请求回调
     */
    public static void sendOkHttpRequestPost(String address, RequestBody requestBody, okhttp3.Callback callback)
    {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder().url(address).post(requestBody).build();
        client.newCall(request).enqueue(callback);
    }

    /**
     * GET方式访问网络
     *
     * @param address  :请求地址
     * @param callback :请求回调
     */
    public static void sendOkHttpRequestGET(String address, okhttp3.Callback callback)
    {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder().url(address).build();
        client.newCall(request).enqueue(callback);
    }
}

Android完整代码链接:

四、java版本的文件上传下载后台如下:

创建SpringBoot项目,pom.xml文件如下:

  <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>2.0.6.RELEASE</version>
   </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

application.yml如下:

server:
  port: 10086

spring:
  application:
    name: aboutfile
  servlet:
    multipart:
      enabled: true #启用文件上传,默认为true
      max-file-size: 10MB  #单个文件最大值(KB/MB)
      max-request-size:  10MB #单个请求多个文件的最大值(KB/MB)
      file-size-threshold: 0KB #文件大于该阈值时,将写入磁盘(KB/MB)



主文件如下:

@SpringBootApplication
public class FileUploadForJavaApp
{
    public static void main(String[] args)
    {
        SpringApplication.run(FileUploadForJavaApp.class, args);
    }
}

 

@Controller
public class UploadController
{
    @Autowired
    private FileManageService fileManageService;

    @PostMapping("/upload")
    @ResponseBody
    public String fileUpload(@RequestParam("file") MultipartFile file) throws Exception
    {
        if (Objects.isNull(file))
        {
            return "文件上传失败,请重新选择文件";
        }

        return fileManageService.upload(file);
    }
}
@Controller
public class DownLoadController
{
    @Autowired
    private FileManageService fileManageService;

    @GetMapping("/download")
    @ResponseBody
    public String fileDownload(@RequestParam("fileName") String fileName, HttpServletResponse response)
    {
        if (Objects.isNull(fileName))
        {
            return "文件下载失败,请选择文件要下载的文件";
        }
        return fileManageService.download(fileName, response);
    }
}

@Service
public class FileManageService
{
    private static final Logger LOGGER = LoggerFactory.getLogger(FileManageService.class);

    private String fileHostPath = "C:\\test\\";

    /**
     * 上传文件
     *
     * @param file
     * @return
     */
    public String upload(MultipartFile file) throws Exception
    {
        String fileName = file.getOriginalFilename(); //获取文件名
        File test = new File(fileHostPath + fileName);
        String[] ext = fileName.split("\\.");//获取后缀名。如果是txt,就要设置为追加。否则直接上传
        if (ext[1].equals("txt") && test.exists())
        {//如果是以txt结尾的face.txt文件,如果存在就追加
            InputStream fis = file.getInputStream();
            String content = readInputStream(fis);
            String[] split = content.split(" ");
            fis = getStringStream(split[split.length - 1] + " "); //只取最后一个
            File fileExist = new File(fileHostPath + fileName);
            OutputStream fos = new FileOutputStream(fileExist, true);
            byte[] flush = new byte[1024];
            int len = -1;
            while ((len = fis.read(flush)) != -1)
            {
                fos.write(flush, 0, len);
            }
            fis.close();
            fos.close();
        } else
        {
            test.createNewFile();
            file.transferTo(test);
        }
        return "文件上传成功";
    }

    /**
     * 把输入流的内容转化成字符串
     *
     * @param is
     * @return 字符串
     */
    public static String readInputStream(InputStream is)
    {
        try
        {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int length = 0;
            byte[] buffer = new byte[1024];
            while ((length = is.read(buffer)) != -1)
            {
                baos.write(buffer, 0, length);
            }
            is.close();
            baos.close();
            //或者用这种方法
            //byte[] result=baos.toByteArray();
            //return new String(result);
            return baos.toString();
        } catch (Exception e)
        {
            e.printStackTrace();
            return "获取失败";
        }
    }

    /**
     * 把字符串转为输入流
     *
     * @param sInputString
     * @return
     */
    public static InputStream getStringStream(String sInputString)
    {
        if (sInputString != null && !sInputString.trim().equals(""))
        {
            try
            {
                ByteArrayInputStream tInputStringStream = new ByteArrayInputStream(sInputString.getBytes());

                return tInputStringStream;
            } catch (Exception ex)
            {
                ex.printStackTrace();
            }
        }
        return null;
    }


    /****
     * 下载文件
     * @param fileName
     * @param response
     * @return
     */
    public String download(String fileName, HttpServletResponse response)
    {
        File file = new File(fileHostPath + fileName);
        if (!file.exists())
        {
            return "文件不存在";
        }
        byte[] bytes = new byte[1024];
        BufferedInputStream bufferedInputStream = null;
        OutputStream outputStream = null;
        FileInputStream fileInputStream = null;
        try
        {
            fileInputStream = new FileInputStream(file);
            bufferedInputStream = new BufferedInputStream(fileInputStream);

            response.setContentType(MediaType.APPLICATION_OCTET_STREAM.toString());
            response.addHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, "UTF-8"));
            outputStream = response.getOutputStream();
            int length;
            while ((length = bufferedInputStream.read(bytes)) != -1)
            {
                outputStream.write(bytes, 0, length);
            }
            outputStream.flush();
        } catch (Exception e)
        {
            LOGGER.error("文件下载失败", e);
        } finally
        {
            try
            {
                if (bufferedInputStream != null)
                {
                    bufferedInputStream.close();
                }

                if (outputStream != null)
                {
                    outputStream.close();
                }

                if (fileInputStream != null)
                {
                    fileInputStream.close();
                }
            } catch (IOException e)
            {
                LOGGER.error(e.getMessage(), e);
            }
        }
        return "文件下载成功!";
    }
}

完整项目下载链接:

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
虹软SDK是一种人脸识别技术,它广泛应用于各种领域,包括安防监控、社交娱乐、金融支付等。在CSND上,可以找到丰富的关于虹软SDK人脸识别的技术文章和开发案例。 虹软SDK人脸识别技术具有高性能、高准确性和高稳定性的特点。它采用深度学习算法,通过对人脸进行特征分析和匹配,能够实现快速、精准地识别人脸。该技术还可以检测人脸的关键点,如眼睛、鼻子、嘴巴等,用于表情分析和人脸特征提取。 虹软SDK人脸识别还具有优良的适应能力。它能够在复杂的环境条件下进行准确的人脸检测和识别识别效果不受光线、角度、年龄、肤色等因素的限制。同时,虹软SDK还支持多人同时识别,可以实现快速的批量人脸识别。 在CSND上,有许多文章介绍了如何使用虹软SDK实现人脸识别功能。这些文章详细介绍了虹软SDK的安装使用步骤、API接口的调用方法及其返回结果的解析。同时,还提供了许多开发案例和技术分享,包括人脸识别的图像处理方法、模型训练技巧和优化策略等。 在虹软SDK人脸识别的开发过程中,CSND是一个宝贵的资源平台。开发者可以通过学习和参考这些文章和案例,快速掌握虹软SDK的使用方法,并且通过技术交流平台与其他开发者进行问题讨论和资源共享。这将有助于提高人脸识别系统的开发效率和质量,促进技术的快速应用和推广。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值