您的位置:首页 > 移动开发 > Android开发

android对PDF文件的操作(上传、预览、下载和转存相册)

2020-07-31 16:04 711 查看

android对PDF文件的操作(上传、预览、下载和转存相册)

一、上传PDF文件到服务器

       前段时间有一个老项目需要添加对PDF的上传、下载和预览等操作,我这边完成之后整理发一下博客,因为项目较老,框架技术也不是先进技术。

       上传文件主要使用了OkGo网络请求框架,如果是使用OKhttp的将网络请求代码和回调方法改成相应的即可,因为上传文件需要用到网络请求权限和文件读写权限,记得需要在AndroidManifest.xml中配置:

<!--用于访问网络-->
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<!--用于写入缓存数据到扩展存储卡-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<!--用于读取扩展存储卡文件-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>

       上传文件代码:

       (1)点击按钮调用打开手机的文件选择器:

// 打开系统的文件选择器
public void pickFile(View view) {
int permission = ActivityCompat.checkSelfPermission(this,
Manifest.permission.READ_EXTERNAL_STORAGE);//缺少什么权限就写什么权限
if (permission != PackageManager.PERMISSION_GRANTED) {
// We don't have permission so prompt the user
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,}, 0);
}
int permission2 = ActivityCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE);//缺少什么权限就写什么权限
if (permission2 != PackageManager.PERMISSION_GRANTED) {
// We don't have permission so prompt the user
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,}, 0);
}
else{
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
//intent.setType("image/*");//选择图片
//intent.setType("audio/*"); //选择音频
//intent.setType("video/*"); //选择视频 (mp4 3gp 是android支持的视频格式)
//intent.setType("video/*;image/*");//同时选择视频和图片
intent.setType("*/*");//无类型限制
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent, 1);
}
}

       (2)回调方法获取文件的真实路径,根据android的版本不同,采用不同的方法

// 获取文件的真实路径
String path;

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
Uri uri = data.getData();
if ("file".equalsIgnoreCase(uri.getScheme())) {//使用第三方应用打开
path = uri.getPath();
ShowDiglog(path);
System.out.println("获得地址1"+path);
return;
}
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {//4.4以后
path = getPath(this, uri);
System.out.println("获得地址2"+path);
ShowDiglog(path);
} else {//4.4以下下系统调用方法
path = getRealPathFromURI(uri);
System.out.println("获得地址3"+path);
ShowDiglog(path);
}
}
}

public String getRealPathFromURI(Uri contentUri) {
String res = null;
String[] proj = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(contentUri, proj, null, null, null);
if (null != cursor && cursor.moveToFirst()) {
;
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
res = cursor.getString(column_index);
cursor.close();
}
return res;
}

/**
* 专为Android4.4设计的从Uri获取文件绝对路径,以前的方法已不好使
*/
@SuppressLint("NewApi")
public String getPath(final Context context, final Uri uri) {

final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];

if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {

final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];

Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}

final String selection = "_id=?";
final String[] selectionArgs = new String[]{split[1]};

return getDataColumn(context, contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}

/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* @param context       The context.
* @param uri           The Uri to query.
* @param selection     (Optional) Filter used in the query.
* @param selectionArgs (Optional) Selection arguments used in the query.
* @return The value of the _data column, which is typically a file path.
*/
public String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {

Cursor cursor = null;
final String column = "_data";
final String[] projection = {column};

try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}

/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
public boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}

/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
public boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}

/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
public boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}

       (3)点击弹窗的上传按钮,调用网络接口,实现文件上传

public void ShowDiglog(final String path){
MyAlertDialog myAlertDialog = new MyAlertDialog(this).builder()
.setTitle("上传简历?")
.setMsg(path)
.setPositiveButton("上传", new View.OnClickListener() {
@Override
public void onClick(View v) {
uploadFile(path);
System.out.println("上传");
}
}).setNegativeButton("取消", new View.OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("取消");
}
});
myAlertDialog.show();
}
public void uploadFile(String path){
File file = new File(path);
OkGo.<String>post(NetUrl.DNS + NetUrl.UploadCv)
.tag(this)
.params("file", file)
.execute(new StringCallback() {
@Override
public void onSuccess(Response<String> response) {
try {
JSONObject jsonObject = new JSONObject(response.body());
System.out.println("输出结果"+jsonObject);
int infoCode = jsonObject.getInt("status");
if (infoCode == 0) {
Tools.toast(IntroFileActivity.this, "简历上传成功!");
introAdapter.clear();
pageNumber = 1;
initData(pageNumber);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}

二、预览网络上的PDF文件

       因为android原生无法直接预览PDF文件,所有简单的就是将PDF文件下载下来转成图片进行预览,但是这样子下载到用户的手机中对用户不友好。在考虑之后,决定抵赖第三方的依赖来实现,需要引入pdfviewpager组件,在gradle组件中添加以下代码:

       (1)添加依赖:

//预览pdf文件
compile('es.voghdev.pdfviewpager:library:1.0.3') {
exclude module: 'support-v4'
exclude group: 'com.android.support'
}

       (2)界面设计(引入依赖的界面组件):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ptr="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/theme_background"
android:orientation="vertical">

<!-- toolbar菜单 -->

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/tj_toolbar_height"
android:background="@color/tj_toolbar_background"
android:orientation="vertical" >

<ImageView
android:id="@+id/toolbar_back"
android:layout_width="50dp"
android:layout_height="match_parent"
android:background="@drawable/tj_click_selector"
android:contentDescription="@null"
android:scaleType="center"
android:src="@drawable/btn_navbar_back" />

<TextView
android:id="@+id/toolbar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="@string/activity_invoice_file_title"
android:textColor="@color/tj_toolbar_title"
android:textSize="@dimen/tj_toolbar_title" />

<TextView
android:id="@+id/textView_download"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:background="@drawable/tj_click_selector"
android:gravity="center"
android:text="@string/activity_invoice_file_download"
android:textColor="@color/tj_text_content_d"
android:textSize="16sp" />

<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_alignParentBottom="true"
android:background="@color/tj_device_line_2" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/remote_pdf_root"
android:layout_width="match_parent"
android:layout_height="match_parent">

<es.voghdev.pdfviewpager.library.PDFViewPager
android:id="@+id/pdfViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />

</RelativeLayout>

<ProgressBar
android:id="@+id/pb_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center" />
</LinearLayout>

       显示效果如图,实际手机中不会有PDF图标:

       (3)Activity代码:

public class ActivityInvoiceFile extends AppCompatActivity implements  DownloadFile.Listener,OnClickListener {

private RelativeLayout pdf_root;
private ProgressBar pb_bar;
private TextView TextViewDownload;
private RemotePDFViewPager remotePDFViewPager;
private String pdfUrl = "";
private String invoiceNo = "";
private PDFPagerAdapter adapter;
private Context mContext;
private String destFileDir;
private String destFileName;
protected RequestCall mRequest;
private ProgressDialog progressDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_invoice_file);
mContext = ActivityInvoiceFile.this;

setupView();

setupData();

setDownloadListener();
}

private void setupView() {
findViewById(R.id.toolbar_back).setOnClickListener(this);
pdf_root = (RelativeLayout) findViewById(R.id.remote_pdf_root);
pb_bar = (ProgressBar) findViewById(R.id.pb_bar);
}

protected void setupData() {
Intent intent = getIntent();
//pdfUrl 为文件地址,比如www.xxx.com/file/xxx.pdf
pdfUrl = intent.getStringExtra("pdfUrl");
}

/*设置监听*/
protected void setDownloadListener() {
final DownloadFile.Listener listener = this;
remotePDFViewPager = new RemotePDFViewPager(this, pdfUrl, listener);
remotePDFViewPager.setId(R.id.pdfViewPager);
}

/*加载成功调用*/
@Override
public void onSuccess(String url, String destinationPath) {
pb_bar.setVisibility(View.GONE);
adapter = new PDFPagerAdapter(this, FileUtil.extractFileNameFromURL(url));
remotePDFViewPager.setAdapter(adapter);
updateLayout();
}

/*更新视图*/
private void updateLayout() {
pdf_root.removeAllViewsInLayout();
pdf_root.addView(remotePDFViewPager, LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
}

/*加载失败调用*/
@Override
public void onFailure(Exception e) {
pb_bar.setVisibility(View.GONE);
Toast.makeText(mContext, "文件加载失败", Toast.LENGTH_SHORT).show();
}
}

三、PDF文件的下载和转存相册

       其实下载文件倒是简单,就是简单的文件下载方式,这里附上一个文件下载工具类,对异常处理和回调进行封装。

       (1)文件下载工具类:

/**
* 文件下载工具类(单例模式)
*/

public class DownloadUtil {
private static DownloadUtil downloadUtil;
private final OkHttpClient okHttpClient;

public static DownloadUtil get() {
if (downloadUtil == null) {
downloadUtil = new DownloadUtil();
}
return downloadUtil;
}

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

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

@Override
public void onResponse(Call call, Response response) throws IOException {
InputStream is = null;
byte[] buf = new byte[2048];
int len = 0;
FileOutputStream fos = null;
// 储存下载文件的目录
File dir = new File(destFileDir);
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(dir, destFileName);
try {
is = response.body().byteStream();
long total = response.body().contentLength();
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(file);
} catch (Exception e) {
listener.onDownloadFailed(e);
} finally {
try {
if (is != null)
is.close();
} catch (IOException e) {
}
try {
if (fos != null)
fos.close();
} catch (IOException e) {
}
}
}
});
}

public interface OnDownloadListener {
/**
* @param file 下载成功后的文件
*/
void onDownloadSuccess(File file);

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

/**
* @param e 下载异常信息
*/
void onDownloadFailed(Exception e);
}
}

       (2)文件下载和转存相册

       在文件下载之后,回调方法返回其真实路径,通过真实路径获取到PDF文件,并将其转成Bitmap,在进一步保存图片到手机中,最后将图片文件插入到系统图库,实现了转存相册功能。Activity完整代码如下:

public class ActivityInvoiceFile extends AppCompatActivity implements  DownloadFile.Listener,OnClickListener {

private RelativeLayout pdf_root;
private ProgressBar pb_bar;
private TextView TextViewDownload;
private RemotePDFViewPager remotePDFViewPager;
private String pdfUrl = "";
private String invoiceNo = "";
private PDFPagerAdapter adapter;
private Context mContext;
private String destFileDir;
private String destFileName;
protected RequestCall mRequest;
private ProgressDialog progressDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_invoice_file);
mContext = ActivityInvoiceFile.this;

setupView();

setupData();

setDownloadListener();
}

private void setupView() {
findViewById(R.id.toolbar_back).setOnClickListener(this);
pdf_root = (RelativeLayout) findViewById(R.id.remote_pdf_root);
pb_bar = (ProgressBar) findViewById(R.id.pb_bar);
TextViewDownload = (TextView)findViewById(R.id.textView_download);
TextViewDownload.setOnClickListener(this);
}

protected void setupData() {
Intent intent = getIntent();
pdfUrl = intent.getStringExtra("pdfUrl");
invoiceNo = intent.getStringExtra("invoiceNo");
}

/*设置监听*/
protected void setDownloadListener() {
final DownloadFile.Listener listener = this;
remotePDFViewPager = new RemotePDFViewPager(this, pdfUrl, listener);
remotePDFViewPager.setId(R.id.pdfViewPager);
}

/*加载成功调用*/
@Override
public void onSuccess(String url, String destinationPath) {
pb_bar.setVisibility(View.GONE);
adapter = new PDFPagerAdapter(this, FileUtil.extractFileNameFromURL(url));
remotePDFViewPager.setAdapter(adapter);
updateLayout();
}

/*更新视图*/
private void updateLayout() {
pdf_root.removeAllViewsInLayout();
pdf_root.addView(remotePDFViewPager, LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
}

/*加载失败调用*/
@Override
public void onFailure(Exception e) {
pb_bar.setVisibility(View.GONE);
Toast.makeText(mContext, "文件加载失败", Toast.LENGTH_SHORT).show();
}

@Override
public void onProgressUpdate(int progress, int total) {
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.toolbar_back:
finish();
break;
case R.id.textView_download:
downFile(pdfUrl);
break;
default:
break;
}
}
/**
* 文件下载
*
* @param url
*/
public void downFile(String url) {
progressDialog = new ProgressDialog(ActivityInvoiceFile.this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setProgress(0);
progressDialog.setMax(100);
progressDialog.show();
progressDialog.setCancelable(true);
DownloadUtil.get().download(url, Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator+"Invoices", "Invoice"+invoiceNo+".pdf", new DownloadUtil.OnDownloadListener() {
@Override
public void onDownloadSuccess(File file) {
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
Looper.prepare();//增加部分
AlertsavePhoto("已下载至手机内部存储设备根目录下Invoices文件夹中","是否转存图片至相册?",file);
Looper.loop();//增加部分
}

@Override
public void onDownloading(int progress) {
progressDialog.setProgress(progress);
}

@Override
public void onDownloadFailed(Exception e) {
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
Looper.prepare();//增加部分
Toast.makeText(mContext, "文件下载失败", Toast.LENGTH_SHORT).show();
Looper.loop();//增加部分
}
});
}
//转存图片对话框
private void AlertsavePhoto(String title, String msg, final File file) {
new Builder(mContext)
.setTitle(title)
.setMessage(msg)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
ArrayList<Bitmap> bitmap = pdfToBitmap(file);
saveImageToGallery(mContext,bitmap);
}
})
.create()
.show();
}

//PDF转成Bitmap
private ArrayList<Bitmap> pdfToBitmap(File pdfFile) {
ArrayList<Bitmap> bitmaps = new ArrayList<>();
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
PdfRenderer renderer = new PdfRenderer(ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_ONLY));
Bitmap bitmap;
final int pageCount = renderer.getPageCount();
Log.e("test_sign", "图片de 张数: " +pageCount);
for (int i = 0; i < pageCount; i++) {
PdfRenderer.Page page = renderer.openPage(i);
int width = getResources().getDisplayMetrics().densityDpi / 72 * page.getWidth();
int height = getResources().getDisplayMetrics().densityDpi / 72 * page.getHeight();
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
//todo 以下三行处理图片存储到本地出现黑屏的问题,这个涉及到背景问题
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(Color.WHITE);
canvas.drawBitmap(bitmap, 0, 0, null);
Rect r = new Rect(0, 0, width, height);
page.render(bitmap, r, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
bitmaps.add(bitmap);
// close the page
page.close();
}
// close the renderer
renderer.close();
}

} catch (Exception ex) {
ex.printStackTrace();
}

return bitmaps;

}
private void saveImageToGallery(Context context, ArrayList<Bitmap> bitmaps) {
// 首先保存图片
File appDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "InvoicePhoto");
for (int i = 0; i < bitmaps.<
20000
/span>size(); i++) {
if (!appDir.exists()) {
appDir.mkdir();
}
String fileName = System.currentTimeMillis() + ".jpg";
File file = new File(appDir, fileName);
Log.e("test_sign", "图片全路径localFile = " + appDir.getAbsolutePath() + fileName);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
bitmaps.get(i).compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
Toast.makeText(mContext, "保存到相册失败!", Toast.LENGTH_LONG).show();
e.printStackTrace();
} catch (IOException e) {
Toast.makeText(mContext, "保存到相册失败!", Toast.LENGTH_LONG).show();
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
//回收
bitmaps.get(i).recycle();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 其次把文件插入到系统图库
try {
MediaStore.Images.Media.insertImage(context.getContentResolver(),
file.getAbsolutePath(), fileName, null);
} catch (FileNotFoundException e) {
Toast.makeText(mContext, "保存到相册失败!", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
Toast.makeText(mContext, "已保存到手机相册!", Toast.LENGTH_LONG).show();
// 最后通知图库更新
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
Uri.fromFile(new File(appDir.getPath()))));
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: