mirror of
https://github.com/gitbucket/gitbucket.git
synced 2026-01-07 16:12:17 +01:00
(refs #1101)Add original GitLFS Transfer API implementation
This commit is contained in:
@@ -57,7 +57,7 @@ abstract class ControllerBase extends ScalatraFilter
|
||||
// Redirect to dashboard
|
||||
httpResponse.sendRedirect(baseUrl + "/")
|
||||
}
|
||||
} else if(path.startsWith("/git/")){
|
||||
} else if(path.startsWith("/git/") || path.startsWith("/git-lfs/")){
|
||||
// Git repository
|
||||
chain.doFilter(request, response)
|
||||
} else {
|
||||
|
||||
@@ -315,9 +315,9 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
}.toMap
|
||||
|
||||
response.setContentLength(attrs("size").toInt)
|
||||
val hash = attrs("oid").split(":")(1)
|
||||
val oid = attrs("oid").split(":")(1)
|
||||
|
||||
using(new FileInputStream(Directory.LfsHome + "/" + hash.substring(0, 2) + "/" + hash.substring(2, 4) + "/" + hash)){ in =>
|
||||
using(new FileInputStream(Directory.LfsHome + "/" + oid.substring(0, 2) + "/" + oid.substring(2, 4) + "/" + oid)){ in =>
|
||||
IOUtils.copy(in, response.getOutputStream)
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package gitbucket.core.servlet
|
||||
|
||||
import java.io.{File, FileInputStream, FileOutputStream}
|
||||
import java.text.MessageFormat
|
||||
import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse}
|
||||
|
||||
import gitbucket.core.util.Directory
|
||||
import org.apache.commons.io.{FileUtils, IOUtils}
|
||||
import org.json4s.jackson.Serialization._
|
||||
import org.apache.http.HttpStatus
|
||||
import gitbucket.core.util.ControlUtil._
|
||||
|
||||
/**
|
||||
* Provides GitLFS Transfer API
|
||||
* https://github.com/git-lfs/git-lfs/blob/master/docs/api/basic-transfers.md
|
||||
*/
|
||||
class GitLfsTransferServlet extends HttpServlet {
|
||||
|
||||
private implicit val jsonFormats = gitbucket.core.api.JsonFormat.jsonFormats
|
||||
private val LongObjectIdLength = 32
|
||||
private val LongObjectIdStringLength = LongObjectIdLength * 2
|
||||
|
||||
override protected def doGet(req: HttpServletRequest, res: HttpServletResponse): Unit = {
|
||||
for {
|
||||
oid <- getObjectId(req, res)
|
||||
} yield {
|
||||
val file = new File(Directory.LfsHome + "/" + oid.substring(0, 2) + "/" + oid.substring(2, 4) + "/" + oid)
|
||||
if(file.exists()){
|
||||
res.setStatus(HttpStatus.SC_OK)
|
||||
res.setContentType("application/octet-stream")
|
||||
res.setContentLength(file.length.toInt)
|
||||
using(new FileInputStream(file), res.getOutputStream){ (in, out) =>
|
||||
IOUtils.copy(in, out)
|
||||
out.flush()
|
||||
}
|
||||
} else {
|
||||
sendError(res, HttpStatus.SC_NOT_FOUND,
|
||||
MessageFormat.format("Object ''{0}'' not found", oid))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override protected def doPut(req: HttpServletRequest, res: HttpServletResponse): Unit = {
|
||||
for {
|
||||
oid <- getObjectId(req, res)
|
||||
} yield {
|
||||
val file = new File(Directory.LfsHome + "/" + oid.substring(0, 2) + "/" + oid.substring(2, 4) + "/" + oid)
|
||||
FileUtils.forceMkdir(file.getParentFile)
|
||||
using(req.getInputStream, new FileOutputStream(file)){ (in, out) =>
|
||||
IOUtils.copy(in, out)
|
||||
}
|
||||
res.setStatus(HttpStatus.SC_OK)
|
||||
}
|
||||
}
|
||||
|
||||
private def getObjectId(req: HttpServletRequest, rsp: HttpServletResponse): Option[String] = {
|
||||
val info: String = req.getPathInfo
|
||||
val length: Int = 1 + LongObjectIdStringLength
|
||||
if (info.length != length) {
|
||||
sendError(rsp, HttpStatus.SC_UNPROCESSABLE_ENTITY,
|
||||
MessageFormat.format("Invalid pathInfo ''{0}'' does not match ''/'{'SHA-256'}'''", info))
|
||||
None
|
||||
} else {
|
||||
Some(info.substring(1, length))
|
||||
}
|
||||
}
|
||||
|
||||
private def sendError(res: HttpServletResponse, status: Int, message: String): Unit = {
|
||||
res.setStatus(status)
|
||||
using(res.getWriter()){ out =>
|
||||
out.write(write(GitLfs.Error(message)))
|
||||
out.flush()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,10 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides GitLFS Batch API
|
||||
* https://github.com/git-lfs/git-lfs/blob/master/docs/api/batch.md
|
||||
*/
|
||||
protected def serviceGitLfsBatchAPI(req: HttpServletRequest, res: HttpServletResponse): Unit = {
|
||||
val batchRequest = read[GitLfs.BatchRequest](req.getInputStream)
|
||||
val settings = loadSystemSettings()
|
||||
@@ -96,10 +100,10 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
||||
}
|
||||
|
||||
res.setContentType("application/vnd.git-lfs+json")
|
||||
|
||||
val out = res.getWriter
|
||||
out.print(write(batchResponse))
|
||||
out.flush()
|
||||
using(res.getWriter){ out =>
|
||||
out.print(write(batchResponse))
|
||||
out.flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -317,4 +321,8 @@ object GitLfs {
|
||||
expires_at: Date
|
||||
)
|
||||
|
||||
case class Error(
|
||||
message: String
|
||||
)
|
||||
|
||||
}
|
||||
@@ -20,6 +20,20 @@ object ControlUtil {
|
||||
}
|
||||
}
|
||||
|
||||
def using[A <% { def close(): Unit }, B <% { def close(): Unit }, C](resource1: A, resource2: B)(f: (A, B) => C): C =
|
||||
try f(resource1, resource2) finally {
|
||||
if(resource1 != null){
|
||||
ignoring(classOf[Throwable]) {
|
||||
resource1.close()
|
||||
}
|
||||
}
|
||||
if(resource2 != null){
|
||||
ignoring(classOf[Throwable]) {
|
||||
resource2.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def using[T](git: Git)(f: Git => T): T =
|
||||
try f(git) finally git.getRepository.close()
|
||||
|
||||
|
||||
@@ -46,6 +46,17 @@
|
||||
<url-pattern>/git/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>GitLfsTransferServlet</servlet-name>
|
||||
<servlet-class>gitbucket.core.servlet.GitLfsTransferServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>GitLfsTransferServlet</servlet-name>
|
||||
<url-pattern>/git-lfs/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
|
||||
<!-- ===================================================================== -->
|
||||
<!-- Supply assets which are provided by plugins -->
|
||||
<!-- ===================================================================== -->
|
||||
|
||||
Reference in New Issue
Block a user