这是我的第一个Kotlin项目,距我开始至今已经3个月了。我正在制作社交应用。现在,我很难编写用于在Firebase存储和数据库上记录和上传视频文件的代码。以下是我的视频帖子创建活动的代码,该活动处理上传和发布。我尝试了许多修改,但是只要我点击创建新帖子或上传视频,应用都会立即在此代码上崩溃。能否请您检查这里有什么问题。因为我的目的只是录制视频,并写下食物名称,地点等,然后发布帖子。我实际上对我的代码感到困惑。
import android.app.Activity
import android.app.ProgressDialog
import android.content.Intent
import android.database.Cursor
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.text.TextUtils
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.android.gms.tasks.Continuation
import com.google.android.gms.tasks.OnCompleteListener
import com.google.android.gms.tasks.Task
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.storage.FirebaseStorage
import com.google.firebase.storage.StorageReference
import com.google.firebase.storage.StorageTask
import com.google.firebase.storage.UploadTask
import kotlinx.android.synthetic.main.activity_add_post.*
class AddPostActivity : AppCompatActivity() {
private var myUrl = ""
private var videoUri: Uri? = null
private var storagePostPicRef: StorageReference? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_post)
storagePostPicRef = FirebaseStorage.getInstance().reference.child("Posts Videos")
save_new_post_btn.setOnClickListener { uploadImage() }
intent.action = MediaStore.ACTION_VIDEO_CAPTURE
startActivityForResult(intent, 101)
// var intent = Intent(MediaStore.ACTION_VIDEO_CAPTURE)
// startActivityForResult(intent, VIDEO_CAPTURE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 101 && resultCode == Activity.RESULT_OK && data != null) {
val result = videoUri
video_new_post.setVideoURI(videoUri)
}
}
private fun getPath(uri: Uri): String {
var projectionArray = arrayOf(MediaStore.Video.Media.DATA)
var cursor: Cursor? =
applicationContext.contentResolver.query(uri, projectionArray, null, null, null)
if (cursor != null) {
val columnIndex: Int = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA)
cursor.moveToFirst()
return cursor.getString(columnIndex)
} else {
return ""
}
}
private fun uploadImage() {
when {
videoUri == null -> Toast.makeText(this, "Please select a Video!", Toast.LENGTH_LONG)
.show()
TextUtils.isEmpty(name_new_post.text.toString()) -> Toast.makeText(
this,
"Please fill all the fields!",
Toast.LENGTH_LONG
).show()
TextUtils.isEmpty(place_new_post.text.toString()) -> Toast.makeText(
this,
"Please fill all the fields!",
Toast.LENGTH_LONG
).show()
TextUtils.isEmpty(city_new_post.text.toString()) -> Toast.makeText(
this,
"Please fill all the fields!",
Toast.LENGTH_LONG
).show()
TextUtils.isEmpty(state_new_post.text.toString()) -> Toast.makeText(
this,
"Please fill all the fields!",
Toast.LENGTH_LONG
).show()
else -> {
val progressDialog = ProgressDialog(this)
progressDialog.setTitle("Upload Started...")
progressDialog.setMessage("Great! Please wait until we bake it...")
progressDialog.show()
val fileRef =
storagePostPicRef!!.child(System.currentTimeMillis().toString() + ".mp4")
var uploadTask: StorageTask<*>
uploadTask = fileRef.putFile(videoUri!!)
uploadTask.continueWithTask(Continuation<UploadTask.TaskSnapshot, Task<Uri>> { task ->
if (!task.isSuccessful) {
task.exception?.let {
throw it
progressDialog.dismiss()
}
}
return@Continuation fileRef.downloadUrl
})
.addOnCompleteListener(OnCompleteListener<Uri> { task ->
if (task.isSuccessful) {
val downloadUrl = task.result
myUrl = downloadUrl.toString()
val ref = FirebaseDatabase.getInstance().reference.child("Posts")
val postId = ref.push().key
val postMap = HashMap<String, Any>()
postMap["postid"] = postId!!
postMap["foodname"] = name_new_post.text.toString().toLowerCase()
postMap["placename"] = place_new_post.text.toString().toLowerCase()
postMap["cityname"] = city_new_post.text.toString().toLowerCase()
postMap["statename"] = state_new_post.text.toString().toLowerCase()
postMap["publisher"] = FirebaseAuth.getInstance().currentUser!!.uid
postMap["postimage"] = myUrl
ref.child(postId).updateChildren(postMap)
Toast.makeText(this, "Upload Finished Successfully!", Toast.LENGTH_LONG)
.show()
val intent = Intent(this@AddPostActivity, MainActivity::class.java)
startActivity(intent)
finish()
progressDialog.dismiss()
} else {
progressDialog.dismiss()
}
})
}
}
}
}
我们正在使用getUriForFile(Context,String,File)返回一个content:// URI。有关面向Android 7.0(API级别)的最新应用24)及更高版本,跨包边界传递file:// URI会导致FileUriExposedException。因此,我们现在提出一个更通用的FileProvider存储图像的方式。
我们创建这样的路径文件:res / xml / file_paths.xml
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="external_files" path="." />
</paths>
代表应用程序外部存储区根目录中的文件。
在manifest.xml中添加提供程序
<manifest>
...
<application>
...
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
...
</application>
</manifest>
这里是一个调用意图以捕获视频的功能。
val REQUEST_VIDEO_CAPTURE = 101
private fun dispatchTakeVideoIntent() {
Intent(MediaStore.ACTION_VIDEO_CAPTURE).also { takeVideoIntent ->
takeVideoIntent.resolveActivity(packageManager)?.also {
val videoFile: File? = try {
createVideoFile()
} catch (ex: IOException) {
null
}
// Continue only if the File was successfully created
videoFile?.also {
videoUri = FileProvider.getUriForFile(
this,
"${your_app_id}.provider",
it
)
takeVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, videoUri)
startActivityForResult(takeVideoIntent, REQUEST_VIDEO_CAPTURE)
}
}
}
}
这是使用日期时间戳为新视频返回唯一文件名的方法中的示例解决方案:
@Throws(IOException::class)
private fun createVideoFile(): File {
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(java.util.Date())
val storageDir: File? = getExternalFilesDir(Environment.DIRECTORY_MOVIES)
return File.createTempFile(
"Video_${timeStamp}_", /* prefix */
".mp4", /* suffix */
storageDir /* directory */
)
}
如果过程成功,则继续
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_VIDEO_CAPTURE && resultCode == RESULT_OK) {
Log.d("VideoPath",videoURI?.path) // you can see video path in log
uploadImage()
}
}
D / VideoPath:/external_files/Movies/Video_20200510_143935_5711184798783973477.mp4
最后,我们对图像上传功能进行了更改。我们需要进行此更改以获取图像下载网址:
if (task.isSuccessful) {
fileRef.downloadUrl().addOnSuccessListener {
myUrl = it.toString()
val ref = FirebaseDatabase.getInstance().reference.child("Posts")
val postId = ref.push().key
val postMap = HashMap<String, Any>()
postMap["postid"] = postId!!
postMap["foodname"] = name_new_post.text.toString().toLowerCase()
postMap["placename"] = place_new_post.text.toString().toLowerCase()
postMap["cityname"] = city_new_post.text.toString().toLowerCase()
postMap["statename"] = state_new_post.text.toString().toLowerCase()
postMap["publisher"] = FirebaseAuth.getInstance().currentUser!!.uid
postMap["postimage"] = myUrl
ref.child(postId).updateChildren(postMap)
Toast.makeText(this, "Upload Finished Successfully!", Toast.LENGTH_LONG)
.show()
progressDialog.dismiss()
/* val intent = Intent(this@AddPostActivity, MainActivity::class.java)
startActivity(intent)
finish()*/
// if you want to return MainActivity just use finish()
finish()
}
} else {
progressDialog.dismiss()
}