无法下载小文件但可以下载大文件

时间:2017-01-26 15:15:44

标签: servlets grails amazon-s3 groovy

我正在使用以下代码从S3下载文件。该代码适用于中型到大型文件,但是,对于极小的文件大小(3kb - TXT文件中的一行),下载失败。

//控制器

Option Explicit On 


Public Class RichTextBoxPrintCtrl
  Inherits RichTextBox


  Private Const AnInch As Double = 14.4

  <StructLayout(LayoutKind.Sequential)> _
   Private Structure RECT
     Public Left As Integer
     Public Top As Integer
     Public Right As Integer
     Public Bottom As Integer
  End Structure

  <StructLayout(LayoutKind.Sequential)> _
  Private Structure CHARRANGE
     Public cpMin As Integer          ' First character of range (0 for start of doc)
     Public cpMax As Integer          ' Last character of range (-1 for end of doc)
  End Structure

  <StructLayout(LayoutKind.Sequential)> _
  Private Structure FORMATRANGE
     Public hdc As IntPtr             ' Actual DC to draw on
     Public hdcTarget As IntPtr       ' Target DC for determining text formatting
     Public rc As Rect                ' Region of the DC to draw to (in twips)
     Public rcPage As Rect            ' Region of the whole DC (page size) (in twips)
     Public chrg As CHARRANGE         ' Range of text to draw (see above declaration)
  End Structure

  Private Const WM_USER As Integer = &H400
  Private Const EM_FORMATRANGE As Integer = WM_USER + 57

  Private Declare Function SendMessage Lib "USER32" Alias "SendMessageA" (ByVal hWnd As IntPtr, ByVal msg As Integer, ByVal wp As IntPtr, ByVal lp As IntPtr) As IntPtr

  ' Render the contents of the RichTextBox for printing
  'Return the last character printed + 1 (printing start from this point for next page)
  Public Function Print(ByVal charFrom As Integer, ByVal charTo As Integer, ByVal e As PrintPageEventArgs) As Integer

     ' Mark starting and ending character 
     Dim cRange As CHARRANGE
     cRange.cpMin = charFrom
     cRange.cpMax = charTo

     ' Calculate the area to render and print
     Dim rectToPrint As RECT
     rectToPrint.Top = e.MarginBounds.Top * AnInch
     rectToPrint.Bottom = e.MarginBounds.Bottom * AnInch
     rectToPrint.Left = e.MarginBounds.Left * AnInch
     rectToPrint.Right = e.MarginBounds.Right * AnInch

     ' Calculate the size of the page
     Dim rectPage As RECT
     rectPage.Top = e.PageBounds.Top * AnInch
     rectPage.Bottom = e.PageBounds.Bottom * AnInch
     rectPage.Left = e.PageBounds.Left * AnInch
     rectPage.Right = e.PageBounds.Right * AnInch

     Dim hdc As IntPtr = e.Graphics.GetHdc()

     Dim fmtRange As FORMATRANGE
     fmtRange.chrg = cRange                 ' Indicate character from to character to 
     fmtRange.hdc = hdc                     ' Use the same DC for measuring and rendering
     fmtRange.hdcTarget = hdc               ' Point at printer hDC
     fmtRange.rc = rectToPrint              ' Indicate the area on page to print
     fmtRange.rcPage = rectPage             ' Indicate whole size of page

     Dim res As IntPtr = IntPtr.Zero          

     Dim wparam As IntPtr = IntPtr.Zero
     wparam = New IntPtr(1)

     ' Move the pointer to the FORMATRANGE structure in memory
     Dim lparam As IntPtr = IntPtr.Zero
     lparam = Marshal.AllocCoTaskMem(Marshal.SizeOf(fmtRange))
     Marshal.StructureToPtr(fmtRange, lparam, False)

     ' Send the rendered data for printing 
     res = SendMessage(Handle, EM_FORMATRANGE, wparam, lparam)

     ' Free the block of memory allocated
     Marshal.FreeCoTaskMem(lparam)

     ' Release the device context handle obtained by a previous call
     e.Graphics.ReleaseHdc(hdc)

     ' Return last + 1 character printer
     Return res.ToInt32()
  End Function



Private Sub PrintDocument1_PrintPage(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage
  ' Print the content of the RichTextBox. Store the last character printed.
  checkPrint = RichTextBoxPrintCtrl1.Print(checkPrint, RichTextBoxPrintCtrl1.TextLength, e)

  ' Look for more pages
  If checkPrint < RichTextBoxPrintCtrl1.TextLength Then
     e.HasMorePages = True
  Else
     e.HasMorePages = False
  End If
End Sub

注销小文件:

def download() {
   Request request = Request.get(params.int("id"))
   response.setContentType("application/octet-stream")
   myService.downloadFileFromS3(request.origFileName, response)
}


void downloadFileFromS3(String fileName, HttpServletResponse response) {
    String fullFileNameWithExtension = fileName
    response.setHeader("Content-disposition", "attachment;filename=${fullFileNameWithExtension}")
    InputStream is = getS3Client().getObject(getBucketName(), fullFileNameWithExtension).getObjectContent()
    OutputStream outputStream = response.getOutputStream()
    byte[] buffer = new byte[1024];
    int length
    log.info("This is the length: " + length)
    while ((length = is.read(buffer)) != -1) {
        log.info("Came here with length: " + length)
        outputStream.write(buffer, 0, length)
    }
    is.close()
}

大文件的日志输出:

This is the length: 0
Came here with length: 15
GroovyPagesServlet:  "/WEB-INF/grails-app/views/request/download.gsp" not found

因此,对于小文件,它似乎正在寻找This is the length: 0 Came here with length: 1024 Came here with length: 1024 Came here with length: 1024 Came here with length: 1024 Came here with length: 1024 Came here with length: 1024 Came here with length: 1024 Came here with length: 1024 Came here with length: 1024 Came here with length: 1024 Came here with length: 1024 Came here with length: 531 视图。但它不应该寻找那种观点,因为我把东西放在download.gsp

1 个答案:

答案 0 :(得分:1)

我认为Joshua Moore的评论可能是正确答案。

但是,我建议稍微重构代码以使其更清洁。

1)首先尝试避免将与Web相关的对象(如请求,响应)传递给服务层。您可以通过让服务层方法返回OutputStream来重构此操作。在服务方法中,您只需创建一个ByteArrayOutputStream,它将保存从S3下载的文件的内容。然后,您将该输出流的内容写入控制器中的var paths []*s3.CompletedPart for key, val := range upload_out { //var unique [10000]*s3.CompletedPart //Attempt unique variable names name := &s3.CompletedPart{ // this only does one ETag: &val, PartNumber: &key, } paths = append(paths, name) } 。这样可以更容易地测试并保持代码DRY(即,您现在可以使用此方法从S3下载文件,而无需将文件内容写入响应)。

2)你也应该重命名控制器方法中声明的response.outputStream变量,因为request是一个隐式变量。

<强>为MyService

request

<强> myController的

def amazonWebService
OutputStream downloadFileFromS3(String filename) {
    InputStream inputStream = amazonWebService.getS3("us-east-1").getObject(getBucketName(), filename).getObjectContent()
    OutputStream outputStream = new ByteArrayOutputStream()
    byte[] buffer = new byte[1024];
    int length
    log.info("This is the length: " + length)
    while ((length = inputStream.read(buffer)) != -1) {
        log.info("Came here with length: " + length)
        outputStream.write(buffer, 0, length)
    }
    if (inputStream) inputStream.close()
    return outputStream
}